假设我有一个data.frame或tibble。该对象有几列。有些列是(A
,B
,C
)是平均值,而其他列是标准差(A.sd
,B.sd
,C.sd
)
df <-
data.frame(
A=c(1,2,3),
A.sd=c(0.3, 0.2, 0.1),
B=c(20,2,34),
B.sd=c(2.1, 5.2, 5.1),
C=c(14,26,13),
C.sd=c(1.3, 0.7, 4.5)
)
现在,我要计算变化系数(sd / mean)(这将是df$A.cv = df$A.sd/df$A
,依此类推)。我可以一一做到。但我想知道是否tidyverse
提供了一种更自动的方式来执行此操作。将“平均值”列与“ sd”列匹配的某种方法,以计算“ cv”列。
答案 0 :(得分:1)
您可以按split.default
的第一个字母按列(names(df)
)拆分数据,然后使用imap
生成cv
列。
library(tidyverse)
split.default(df, f = substr(names(df), 1, 1)) %>%
imap(.x = ., ~ mutate(., cv = .x[, paste0(.y, ".sd")] / .x[, .y])) %>%
imap(., ~ set_names(., nm = paste0(.y, c("", ".sd", ".cv")))) %>% # rename the columns
bind_cols()
# A A.sd A.cv B B.sd B.cv C C.sd C.cv
#1 1 0.3 0.30000000 20 2.1 0.105 14 1.3 0.09285714
#2 2 0.2 0.10000000 2 5.2 2.600 26 0.7 0.02692308
#3 3 0.1 0.03333333 34 5.1 0.150 13 4.5 0.34615385
imap
在这里很方便,因为它使您可以轻松地遍历列表并遍历该列表的名称(代码中的.y
)。
这里需要第二个imap
调用,因为这会产生错误
split.default(df, f = substr(names(df), 1, 1)) %>%
imap(.x = ., ~ mutate(., paste0(.y, ".cv") = .x[, paste0(.y, ".sd")] / .x[, .y]))
相同的想法,但在base R
lst <- split.default(df, f = substr(names(df), 1, 1))
Reduce(cbind, Map(
function(x, y)
`[<-`(x, paste0(y, ".cv"), value = x[, paste0(y, ".sd")] / x[, y]),
x = lst,
y = names(lst)
))
答案 1 :(得分:1)
使用tidyverse
和split.default
:
df %>%
split.default(substr(names(.),1,1)) %>%
map_dfc(~mutate(., !!paste0(names(.)[1],".cv") := .[[2]]/.[[1]]))
# A A.sd A.cv B B.sd B.cv C C.sd C.cv
# 1 1 0.3 0.30000000 20 2.1 0.105 14 1.3 0.09285714
# 2 2 0.2 0.10000000 2 5.2 2.600 26 0.7 0.02692308
# 3 3 0.1 0.03333333 34 5.1 0.150 13 4.5 0.34615385
paste0(names(.)[1],".cv")
(A.cv
等)的新列,并将所有内容绑定在一起。在基数R中:
df_list <- unname(split.default(df,substr(names(df),1,1)))
add_cv <- function(x) `[[<-`(x, paste0(names(x)[1], ".cv"), value = x[[2]] / x[[1]])
do.call(cbind, lapply(df_list, add_cv))
# A A.sd A.cv B B.sd B.cv C C.sd C.cv
# 1 1 0.3 0.30000000 20 2.1 0.105 14 1.3 0.09285714
# 2 2 0.2 0.10000000 2 5.2 2.600 26 0.7 0.02692308
# 3 3 0.1 0.03333333 34 5.1 0.150 13 4.5 0.34615385
R基地再次分裂:
df_list <- split.default(df, endsWith(names(df),".sd"))
cbind(df, setNames(df_list[[2]] / df_list[[1]], paste0(names(df_list[[1]]), ".cv")))
# A A.sd B B.sd C C.sd A.cv B.cv C.cv
# 1 1 0.3 20 2.1 14 1.3 0.30000000 0.105 0.09285714
# 2 2 0.2 2 5.2 26 0.7 0.10000000 2.600 0.02692308
# 3 3 0.1 34 5.1 13 4.5 0.03333333 0.150 0.34615385
答案 2 :(得分:0)
您可以执行以下操作:
library(tidyverse)
df %<>% mutate(A.cv=A.sd/A,
B.cv=B.sd/B,
C.cv=C.sd/C)
下面提出了一个更好的解决方案。
答案 3 :(得分:0)
如果将其变成长DF,类似这样的事情相对容易:
library(tidyverse)
df <- data.frame(
groups = rep(c("A", "B", "C"), each = 3),
means = c(1, 2, 3, 20, 2, 34, 14, 26, 13),
sd = c(0.3, 0.2, 0.1, 2.1, 5.2, 5.1, 1.3, 0.7, 4.5)
)
df <- df %>% mutate(
cv = (sd / means)
)
答案 4 :(得分:0)
这是另一种tidyverse
版本:
df <-
data.frame(
A=c(1,2,3),
A.sd=c(0.3, 0.2, 0.1),
B=c(20,2,34),
B.sd=c(2.1, 5.2, 5.1),
C=c(14,26,13),
C.sd=c(1.3, 0.7, 4.5)
)
library(tidyverse)
{df %>% select(matches("sd")) / df %>% select(-matches("sd"))} %>%
setNames(gsub("sd", "cv", names(.))) %>%
bind_cols(df, .)
# A A.sd B B.sd C C.sd A.cv B.cv C.cv
# 1 1 0.3 20 2.1 14 1.3 0.30000000 0.105 0.09285714
# 2 2 0.2 2 5.2 26 0.7 0.10000000 2.600 0.02692308
# 3 3 0.1 34 5.1 13 4.5 0.03333333 0.150 0.34615385
注意,您必须确保列在原始数据集中的顺序正确。
答案 5 :(得分:0)
使用数据df
,您可以使用dplyr
函数ends_with()
将数据集一分为二,转换为long并再次绑定:
library(tidyverse)
df <-
data.frame(
A=c(1,2,3),
A.sd=c(0.3, 0.2, 0.1),
B=c(20,2,34),
B.sd=c(2.1, 5.2, 5.1),
C=c(14,26,13),
C.sd=c(1.3, 0.7, 4.5)
)
sds <- select(df, ends_with(".sd")) %>%
gather() %>%
rename(sd = value) %>%
select(sd)
means <- select(df, -ends_with(".sd")) %>%
gather() %>%
rename(mean = value)
df_n <- bind_cols(means, sds)
df_n <- mutate(df_n, cv = sd/mean)
答案 6 :(得分:0)
我建议进行以下转换:
df %>%
# Adding counter
mutate(n = 1:n()) %>%
# Converting to long format
gather("key", "value", -n) %>%
# Adding variable that distinguishes SD and mean
mutate(type = ifelse(grepl("\\.sd$", key), "SD", "mean"),
item = sub("(\\w).*", "\\1", key), # A, B, or C
case = paste(item, n)) %>% # e.g., A 1, B 2, etc.
select(n, value, type, case) %>%
# Conversion back to wide format
spread("type", "value") %>%
# Calculating COV
mutate(COV = mean / SD)
答案 7 :(得分:0)
只需:
for (ThresholdParams p : threshold.getThresholdParams()) {
if (p !=null) {
thresholdParamsRepository.save(p);
}
}
请注意:
IND <- rep(seq(1:(ncol(df1)/2)),each=2)
df1[paste0(names(df1)[!duplicated(IND,F)], ".cv")] <- lapply(split(as.data.frame(t(df1)), IND), function(x)c(t(x)[,2]/t(x)[,1]))
# A A.sd B B.sd C C.sd A.cv B.cv C.cv
#1 1 0.3 20 2.1 14 1.3 0.30000000 0.105 0.09285714
#2 2 0.2 2 5.2 26 0.7 0.10000000 2.600 0.02692308
#3 3 0.1 34 5.1 13 4.5 0.03333333 0.150 0.34615385
解决方案-无需第三方软件包。如果要依赖名称,可以使用简单的for循环:
Base
答案 8 :(得分:0)
> cv
A.cv B.cv C.cv
1 0.30000000 0.105 0.09285714
2 0.10000000 2.600 0.02692308
3 0.03333333 0.150 0.34615385
显然超级hacky,还有很大的优化空间,但很可能实现了您的目标。
cv <- data.frame()
counter <- 0
for (i in 1:ncol(df))(
if (grepl("sd$", colnames(df)[i]) == TRUE){
counter <- counter + 1
for (j in 1:nrow(df))(
cv[j, counter] <- df[j, i]/df[j, i-1]
)
names(cv)[counter] <- paste0(colnames(df)[i-1],".cv")
}
)
答案 9 :(得分:0)
规范和简约的方法是从宽变长,重新计算CV,然后从长变宽(如有必要)。
library(tidyverse)
df %>%
rowid_to_column("row") %>%
gather(key, value, -row) %>%
mutate(key = str_replace(key, "^([A-Z])$", "\\1.mean")) %>%
separate(key, c("var", "col")) %>%
spread(col, value) %>%
transmute(row, var = paste0(var, ".cv"), cv = sd / mean) %>%
spread(var, cv)
# row A.cv B.cv C.cv
#1 1 0.30000000 0.105 0.09285714
#2 2 0.10000000 2.600 0.02692308
#3 3 0.03333333 0.150 0.34615385
这种方法也与均值/标准差列的顺序无关。
按操作编辑:
df %>%
rowid_to_column("row") %>%
gather(key, value, -row) %>%
mutate(key = str_replace(key, "^([A-Z])$", "\\1.mean")) %>%
separate(key, c("var", "col")) %>%
spread(col, value) %>%
transmute(row, var = paste0(var, ".cv"), cv = sd / mean) %>%
spread(var, cv) %>%
bind_cols(df, .) %>%
select(-row)
这样,结果位于同一数据帧中,而没有“行”列。