提问人:code_rookie 提问时间:10/29/2021 最后编辑:code_rookie 更新时间:10/29/2021 访问量:92
如何从 R 中的多个列创建合并值的新数据帧
How to a create a new dataframe of consolidated values from multiple columns in R
问:
我有一个数据帧 df1,如下所示:
样本 | 99_Ape_1 | 93_Cat_1 | 87_Ape_2 | 84_Cat_2 | 90_Dog_1 | 92_Dog_2 |
---|---|---|---|---|---|---|
一个 | 2 | 3 | 1 | 7 | 4 | 6 |
B | 5 | 9 | 7 | 0 | 3 | 7 |
C | 6 | 8 | 9 | 2 | 3 | 0 |
D | 3 | 9 | 0 | 5 | 8 | 3 |
我想通过根据标题行中存在的动物(即“猿”、“猫”、“狗”)对值求和来合并数据帧,并最终得到以下数据帧:
样本 | 猿 | 猫 | 狗 |
---|---|---|---|
一个 | 3 | 10 | 10 |
B | 12 | 9 | 10 |
C | 15 | 10 | 3 |
D | 3 | 14 | 11 |
我创建了一个列表,代表所有称为“animals_list”的动物
然后,我创建了一个数据帧列表,该列表将每个动物子集到一个单独的数据帧中,其中包含:
animals_extract <- c()
for (i in 1:length(animals_list)){
species_extract[[i]] <- df1[, grep(animals_list[i], names(df1))]
}
然后,我尝试按样本对行中的每个变量求和:
for (i in 1:length(species_extract)){
species_extract[[i]]$total <- rowSums(species_extract[[i]])
}
然后通过绑定新“总计”列中的所有值来创建数据帧“animal_total”。
animal_total <- NULL
for (i in 1:length(species_extract)){
animal_total[i] <- cbind(species_extract[[i]]$total)
}
不幸的是,这似乎根本行不通,我想我可能走错了路。任何帮助将不胜感激!
编辑:我的数据框有 300 多只动物,这意味着使用我的标识符列表 (animals_list) 将不胜感激!我还要指出,有些列名不遵循结构“number_animal_number”,因此我不能使用重复搜索(对不起!
答:
4赞
Wimpel
10/29/2021
#1
一种方法data.table
library(data.table)
library(rlist)
#set data to data.table format
setDT(df1)
# split column 2:n by regex on column names
L <- split.default(df1[,-1], gsub(".*_(.*)_.*", "\\1", names(df1)[-1]))
# Bind together again
data.table(sample = df1$sample,
as.data.table(list.cbind(lapply(L, rowSums))))
# sample Ape Cat Dog
# 1: A 3 10 10
# 2: B 12 9 10
# 3: C 15 10 3
# 4: D 3 14 11
评论
0赞
code_rookie
10/29/2021
谢谢!这正是我要找的,但不幸的是,并非所有列名都具有相同的“number_animal_number”结构。有没有办法使用我创建的名称列表来做同样的事情?对不起,我的问题没有说清楚。
0赞
TarJae
10/29/2021
完善。不知何故,我必须学习 data.table 编码!
0赞
Wimpel
10/30/2021
@code_rookie是可以在正则表达式中捕获想要的列(可能的答案是 Yes ;-)),如果是这样,您可以使用上面的方法。如果你不确定如何做,你可以问一个新问题,要求一个正则表达式,用你的colnames作为样本数据。
3赞
TarJae
10/29/2021
#2
更新:澄清后: 这可能有效,具体取决于您的动物的其他名称。但这只是一个开始:
library(dplyr)
library(tidyr)
df %>%
pivot_longer(
cols = -sample
) %>%
mutate(name1 = str_extract(name, '(?<=\\_)(.*?)(?=\\_)')) %>%
group_by(sample, name1) %>%
summarise(sum=sum(value)) %>%
pivot_wider(
names_from = name1,
values_from= sum
)
输出:
sample Ape Cat Dog
<chr> <int> <int> <int>
1 A 3 10 10
2 B 12 9 10
3 C 15 10 3
4 D 3 14 11
第一个答案:以下是我们如何做到这一点:dplyr
library(dplyr)
df %>%
mutate(Cat = rowSums(select(., contains("Cat"))),
Ape = rowSums(select(., contains("Ape"))),
Dog = rowSums(select(., contains("Dog")))) %>%
select(sample, Cat, Ape, Dog)
sample Ape Cat Dog
<chr> <int> <int> <int>
1 A 3 10 10
2 B 12 9 10
3 C 15 10 3
4 D 3 14 11
评论
1赞
code_rookie
10/29/2021
谢谢!我的整个数据帧大约有 300 只动物,这意味着这种方式的代码会很长。有没有办法使用我创建的名称列表来做同样的事情?对不起,我的问题没有说清楚。我将编辑我的问题以使其明显。
0赞
TarJae
10/29/2021
哦,我明白。我去看看。
0赞
TarJae
10/29/2021
请参阅我的更新。这可能有效,具体取决于其他列名称的模式。告诉我!
0赞
Ben
10/29/2021
这也是我的方法。
1赞
code_rookie
10/29/2021
谢谢你们俩。在我清理了列名以赋予它一个规则模式后,这种方法奏效了,如示例数据帧所示。谢谢!
0赞
Sef
10/29/2021
#3
另一种 data.table 解决方案
library(data.table)
# Construct data table
dt <- as.data.table(list(sample = c("A", "B", "C", "D"),
`99_Ape_1` = c(2, 5, 6, 3),
`93_Cat_1` = c(3, 9, 8, 9),
`87_Ape_2` = c(1, 7, 9, 0),
`84_Cat_2` = c(7, 0, 2, 5),
`90_Dog_1` = c(4, 3, 3, 8),
`92_Dog_2` = c(6, 7, 0, 3)))
# Alternatively convert existing dataframe
# dt <- setDT(df)
# Use Regex pattern to drop ids from column names
names(dt) <- gsub("((^[0-9_]{3})|(_[0-9]{1}$))", "", names(dt))
# Pivot long (columns to rows)
dt <- melt(dt, id.vars = "sample")
# Aggregate sample by variable
dt <- dt[, .(value=sum(value)), by=.(sample, variable)]
# Unpivot (rows to colums)
dcast(dt, sample ~ variable)
# sample Ape Cat Dog
# 1: A 3 10 10
# 2: B 12 9 10
# 3: C 15 10 3
# 4: D 3 14 11
或者,保持列名不变(在从 OP 到上一个答案的注释之后),并假设对同一样本有多个观察值:
dt <- as.data.table(list(sample = c("A", "B", "C", "D", "A"),
`99_Ape_1` = c(2, 5, 6, 3, 1),
`93_Cat_1` = c(3, 9, 8, 9, 1),
`87_Ape_2` = c(1, 7, 9, 0, 1),
`84_Cat_2` = c(7, 0, 2, 5, 1),
`90_Dog_1` = c(4, 3, 3, 8, 1),
`92_Dog_2` = c(6, 7, 0, 3, 1)))
dt
# sample 99_Ape_1 93_Cat_1 87_Ape_2 84_Cat_2 90_Dog_1 92_Dog_2
# 1: A 2 3 1 7 4 6
# 2: B 5 9 7 0 3 7
# 3: C 6 8 9 2 3 0
# 4: D 3 9 0 5 8 3
# 5: A 1 1 1 1 1 1
# Pivot long (columns to rows)
dt <- melt(dt, id.vars = "sample")
# Aggregate sample by variable
dt <- dt[, .(value=sum(value)), by=.(sample, variable)]
# Unpivot (rows to colums)
dcast(dt, sample ~ variable)
# sample 99_Ape_1 93_Cat_1 87_Ape_2 84_Cat_2 90_Dog_1 92_Dog_2
# 1: A 3 4 2 8 5 7
# 2: B 5 9 7 0 3 7
# 3: C 6 8 9 2 3 0
# 4: D 3 9 0 5 8 3
评论
0赞
code_rookie
10/29/2021
谢谢你的回答。我的编辑并不是说我希望保留相同的列名,我希望将它们聚合到基于动物的合并列中,但由于我有很多变量并且它们不遵循重复模式,我希望使用列表“animals_list”来搜索和合并。我希望这是有道理的。
0赞
Sef
10/29/2021
如果动物列表只是与列名匹配的值的字符向量,则答案是有效的。如果它是将列名中的值映射到相关动物种类的数据帧,则需要在透视长转换后进行额外的连接,以允许按目标值而不是列名聚合数据。如果动物清单是所需解决方案的关键部分,您应该在问题中包含一个最小工作示例。
评论