数据框中值的条件划分
Conditional division of values within a data frame
首先让我感叹我是 R 的新手,这对于有经验的用户来说可能很容易。
我一直在试图弄清楚如何在数据框中执行值的条件划分。具体来说,我有一个带有多个标识符的数据框(如下图 df1
)。我想用向量或 df (df2
) 中的值除以具有匹配的唯一标识符(如下所示)的值,同时保持标识符完整。
ID = c("nr6536","nr8798","nr8723","nr8276","nr7774","nr0093")
Conc = c("1:2","1:4","1:2","1:8","1:4","1:4")
ID1 = c("ID","Conc","Eb","Eb","Sd","Sd","Re","Sd")
ID2 = c("Re","Sd","Eb")
dat1 = c(2,3,4,5,6,7)
dat2 = c(8,9,10,11,12,13)
dat3 = c(1,2,3,4,5,6)
dat4 = c(4,3,2,7,6,5)
dat5 = c(8,7,6,3,4,8)
dat6 = c(6,4,2,5,3,8)
dat7 = c(9,2,7)
df1 = data.frame(ID,Conc,dat1,dat2,dat3,dat4,dat5,dat6)
colnames(df1)=ID1
df2 = data.frame(t(dat7)); colnames(df2)=ID2
rm(ID,Conc,ID1,ID2,dat1, dat2,dat3,dat4,dat5,dat6,dat7)
想要的输出:
ID Conc Eb Eb Sd Sd Re Sd
nr6536 1:2 0.2857 1.1429 0.5 2 0.8889 3
nr8798 1:4 0.4286 1.2857 1 1.5 0.7778 2
nr8723 1:2 0.5714 1.4286 1.5 1 0.6667 1
nr8276 1:8 0.7143 1.5714 2 3.5 0.3333 2.5
nr7774 1:4 0.8571 1.7143 2.5 3 0.4444 1.5
nr0093 1:4 1 1.8571 3 2.5 0.8889 4
我已尝试使用 ave
、tabulate
和 ifelse
,但未能获得所需的输出。下面给出了一个非功能性示例:
library(reshape2)
df1.2 = melt(df1)
df2names = colnames(df2)
df2.2 = as.data.frame(t(df2)); df2.2$variable=df2names
df3 = as.data.frame(apply(df1.2,1,function(x){
ScaVal = ifelse(x$variable %in% df2.2$variable, value/df2.2$V1)
return(ScaVal)
}))
有人对如何做到这一点有任何建议吗?提前致谢!
我通常更喜欢 tidyverse
解决方案,但由于您在 df1
中没有唯一的变量名称,您将 运行 遇到问题。相反,有一个使用 for loop
的基本 R 解决方案(令人震惊,我知道!)。不过,除非您的数据框中有数百万列,否则我认为您不会遇到任何性能问题。
conditional_divide <- function(df1, df2) {
for (i in seq_len(ncol(df1))) {
if (colnames(df1)[i] %in% colnames(df2)) {
df1[,i] <- df1[,i] / df2[[colnames(df1)[i]]]
}
}
return(df1)
}
conditional_divide (df1, df2)
ID Conc Eb Eb Sd Sd Re Sd
1 nr6536 1:2 0.2857143 1.142857 0.5 2.0 0.8888889 3.0
2 nr8798 1:4 0.4285714 1.285714 1.0 1.5 0.7777778 2.0
3 nr8723 1:2 0.5714286 1.428571 1.5 1.0 0.6666667 1.0
4 nr8276 1:8 0.7142857 1.571429 2.0 3.5 0.3333333 2.5
5 nr7774 1:4 0.8571429 1.714286 2.5 3.0 0.4444444 1.5
6 nr0093 1:4 1.0000000 1.857143 3.0 2.5 0.8888889 4.0
这是一种使用 purrr::reduce()
(tidyverse
的一部分)的方法。
注意:它确实涉及允许 df1
使用唯一的列名。
library(tidyverse)
df1 <- df1 %>% setNames(make.names(names(.), unique = TRUE))
values <- df2 %>% unlist(.)
update <- function(df, val, name) mutate_at(df, vars(starts_with(name)), funs(./val))
reduce2(values, names(values), update, .init=df1)
输出:
ID Conc Eb Eb.1 Sd Sd.1 Re Sd.2
1 nr6536 1:2 0.285714 1.14286 0.5 2.0 0.888889 3.0
2 nr8798 1:4 0.428571 1.28571 1.0 1.5 0.777778 2.0
3 nr8723 1:2 0.571429 1.42857 1.5 1.0 0.666667 1.0
4 nr8276 1:8 0.714286 1.57143 2.0 3.5 0.333333 2.5
5 nr7774 1:4 0.857143 1.71429 2.5 3.0 0.444444 1.5
6 nr0093 1:4 1.000000 1.85714 3.0 2.5 0.888889 4.0
如果您想将列名恢复到原来的样子(尽管不建议使用非唯一的列名),请使用 rename()
:
purrr::reduce2(values, names(values), update, .init=df1) %>%
rename_at(vars(matches("\.\d")), funs(str_replace(., "\.\d", "")))
首先让我感叹我是 R 的新手,这对于有经验的用户来说可能很容易。
我一直在试图弄清楚如何在数据框中执行值的条件划分。具体来说,我有一个带有多个标识符的数据框(如下图 df1
)。我想用向量或 df (df2
) 中的值除以具有匹配的唯一标识符(如下所示)的值,同时保持标识符完整。
ID = c("nr6536","nr8798","nr8723","nr8276","nr7774","nr0093")
Conc = c("1:2","1:4","1:2","1:8","1:4","1:4")
ID1 = c("ID","Conc","Eb","Eb","Sd","Sd","Re","Sd")
ID2 = c("Re","Sd","Eb")
dat1 = c(2,3,4,5,6,7)
dat2 = c(8,9,10,11,12,13)
dat3 = c(1,2,3,4,5,6)
dat4 = c(4,3,2,7,6,5)
dat5 = c(8,7,6,3,4,8)
dat6 = c(6,4,2,5,3,8)
dat7 = c(9,2,7)
df1 = data.frame(ID,Conc,dat1,dat2,dat3,dat4,dat5,dat6)
colnames(df1)=ID1
df2 = data.frame(t(dat7)); colnames(df2)=ID2
rm(ID,Conc,ID1,ID2,dat1, dat2,dat3,dat4,dat5,dat6,dat7)
想要的输出:
ID Conc Eb Eb Sd Sd Re Sd
nr6536 1:2 0.2857 1.1429 0.5 2 0.8889 3
nr8798 1:4 0.4286 1.2857 1 1.5 0.7778 2
nr8723 1:2 0.5714 1.4286 1.5 1 0.6667 1
nr8276 1:8 0.7143 1.5714 2 3.5 0.3333 2.5
nr7774 1:4 0.8571 1.7143 2.5 3 0.4444 1.5
nr0093 1:4 1 1.8571 3 2.5 0.8889 4
我已尝试使用 ave
、tabulate
和 ifelse
,但未能获得所需的输出。下面给出了一个非功能性示例:
library(reshape2)
df1.2 = melt(df1)
df2names = colnames(df2)
df2.2 = as.data.frame(t(df2)); df2.2$variable=df2names
df3 = as.data.frame(apply(df1.2,1,function(x){
ScaVal = ifelse(x$variable %in% df2.2$variable, value/df2.2$V1)
return(ScaVal)
}))
有人对如何做到这一点有任何建议吗?提前致谢!
我通常更喜欢 tidyverse
解决方案,但由于您在 df1
中没有唯一的变量名称,您将 运行 遇到问题。相反,有一个使用 for loop
的基本 R 解决方案(令人震惊,我知道!)。不过,除非您的数据框中有数百万列,否则我认为您不会遇到任何性能问题。
conditional_divide <- function(df1, df2) {
for (i in seq_len(ncol(df1))) {
if (colnames(df1)[i] %in% colnames(df2)) {
df1[,i] <- df1[,i] / df2[[colnames(df1)[i]]]
}
}
return(df1)
}
conditional_divide (df1, df2)
ID Conc Eb Eb Sd Sd Re Sd
1 nr6536 1:2 0.2857143 1.142857 0.5 2.0 0.8888889 3.0
2 nr8798 1:4 0.4285714 1.285714 1.0 1.5 0.7777778 2.0
3 nr8723 1:2 0.5714286 1.428571 1.5 1.0 0.6666667 1.0
4 nr8276 1:8 0.7142857 1.571429 2.0 3.5 0.3333333 2.5
5 nr7774 1:4 0.8571429 1.714286 2.5 3.0 0.4444444 1.5
6 nr0093 1:4 1.0000000 1.857143 3.0 2.5 0.8888889 4.0
这是一种使用 purrr::reduce()
(tidyverse
的一部分)的方法。
注意:它确实涉及允许 df1
使用唯一的列名。
library(tidyverse)
df1 <- df1 %>% setNames(make.names(names(.), unique = TRUE))
values <- df2 %>% unlist(.)
update <- function(df, val, name) mutate_at(df, vars(starts_with(name)), funs(./val))
reduce2(values, names(values), update, .init=df1)
输出:
ID Conc Eb Eb.1 Sd Sd.1 Re Sd.2
1 nr6536 1:2 0.285714 1.14286 0.5 2.0 0.888889 3.0
2 nr8798 1:4 0.428571 1.28571 1.0 1.5 0.777778 2.0
3 nr8723 1:2 0.571429 1.42857 1.5 1.0 0.666667 1.0
4 nr8276 1:8 0.714286 1.57143 2.0 3.5 0.333333 2.5
5 nr7774 1:4 0.857143 1.71429 2.5 3.0 0.444444 1.5
6 nr0093 1:4 1.000000 1.85714 3.0 2.5 0.888889 4.0
如果您想将列名恢复到原来的样子(尽管不建议使用非唯一的列名),请使用 rename()
:
purrr::reduce2(values, names(values), update, .init=df1) %>%
rename_at(vars(matches("\.\d")), funs(str_replace(., "\.\d", "")))