[英]How Can I create a new variable, based on combining the values of several variables in same column and remove the old variables used when combining
[英]Create several new columns combining old ones by name
假設我有一個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”列。
您可以按names(df)
的第一個字母按列拆分數據( 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
為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)
))
使用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
等)的新列paste0(names(.)[1],".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
您可以執行以下操作:
library(tidyverse)
df %<>% mutate(A.cv=A.sd/A,
B.cv=B.sd/B,
C.cv=C.sd/C)
下面提出了一個更好的解決方案。
如果您將它變成長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)
)
這是另一個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
請注意 ,您必須確保列在原始數據集中的順序正確。
使用數據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)
我建議進行以下轉換:
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)
只需做:
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
請注意:
Base
解決方案-無需第三方軟件包。 如果要依賴名稱,可以使用簡單的for循環:
# name_vec <- LETTERS[1:3]
name_vec <- names(df1)[grepl("^[^.]+$",names(df1))]
for( name_el in name_vec) {
df1[paste0(name_el, ".cv")] <- df1[[paste0(name_el, ".sd")]]/df1[[name_el]]
}
> 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")
}
)
規范和簡約的方法是從寬變長,重新計算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
此方法也與均值/標准差列的順序無關。
按OP編輯:
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)
這樣,結果在同一數據幀中,而沒有“行”列。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.