R:对排序子集的所有行应用函数,如果满足条件则完成计算
R: apply a function to all rows that orders a subset and completes a calculation if a condition is met
我想对每一行应用一个函数,之后
根据 if/else 语句对该行的子集进行排序。然后用结果填充数据框中的新列。我有超过 200 万行,因此执行此操作的 for 循环效率非常低。
给定以下数据框:(为 r2evans 编辑)
df<-as.data.frame(cbind(matrix(LETTERS[1:3], ncol=1),matrix(sample.int(100,size=15),nrow=3,ncol=5))
> df
V1 V2 V3 V4 V5 V6
1 A 77 79 32 5 4
2 B 57 24 68 65 45
3 C 66 60 82 74 22
有没有办法在没有 for 循环的情况下将以下循环应用于每一行,因为我实际上有超过 2630800 行?
df$num <- 0
df[2:7] <- sapply(df[2:7],as.numeric)
names(df) <- c("first_name", "sec", "A", "B", "C", "D", "num")
下面的 if 语句需要列的名称:
(也编辑为只排序一次)
for (i in seq_len(nrow(df))) {
row = sort(df[i,3:6])
if (df[i,1]==names(row)[4]) {
df$num[i] = row[3]/(row[3]+row[4])
} else {
df$num[i] = row[4]/(row[3]+row[4])
}
}
这样我得到了这个结果:
> df
first_name sec A B C D num
1 A 77 79 32 5 4 0.2882883
2 B 57 24 68 65 45 0.4887218
3 C 66 60 82 74 22 0.525641
我不确定如何使用 apply 执行此操作,是否在想这样的事情?尽管这不起作用,而且我不确定如何合并 if/else 条件:
df$num <- apply(df, 1, function(x) unlist(x[3:6][order(x[3:6])][3]/(x[3:6][order(x[3:6])][3]+x[3:6][order(x[3:6])][4])))
这里有一些硬编码(列索引)的小蛮力方法。
cols <- 3:6
sorted2 <- t(apply(df[,cols], 1, sort, decreasing = TRUE))[,1:2]
sorted2
# [,1] [,2]
# 1 79 32
# 2 68 65
# 3 82 74
df$num <- ifelse(df[,1] == names(df)[cols][max.col(df[,cols])],
sorted2[,2], sorted2[,1]) /
rowSums(sorted2)
df
# first_name sec A B C D num
# 1 A 77 79 32 5 4 0.2882883
# 2 B 57 24 68 65 45 0.4887218
# 3 C 66 60 82 74 22 0.5256410
备注:
- 我定义
cols
这样如果你改变列,你只需要改变一次。
- 您的代码倾向于比较
first_name
是否与列名匹配,并据此确定您是使用 cols
列中的“最大值”还是“第二最大值”,以及然后将 that 数字除以两个最大数字的总和。为此,我计算 sorted2
,它始终在第 1 列中具有最大值,在第 2 列中具有第二个最大值,从而使 rowSums
和 max/2nd-max 访问 immediate/easy.
- 我本可以使用
df$first_name
而不是 df[,1]
,你的选择。
这应该比 for
循环或 sapply
工作得更好,因为它是完全矢量化的。
我想对每一行应用一个函数,之后 根据 if/else 语句对该行的子集进行排序。然后用结果填充数据框中的新列。我有超过 200 万行,因此执行此操作的 for 循环效率非常低。
给定以下数据框:(为 r2evans 编辑)
df<-as.data.frame(cbind(matrix(LETTERS[1:3], ncol=1),matrix(sample.int(100,size=15),nrow=3,ncol=5))
> df
V1 V2 V3 V4 V5 V6
1 A 77 79 32 5 4
2 B 57 24 68 65 45
3 C 66 60 82 74 22
有没有办法在没有 for 循环的情况下将以下循环应用于每一行,因为我实际上有超过 2630800 行?
df$num <- 0
df[2:7] <- sapply(df[2:7],as.numeric)
names(df) <- c("first_name", "sec", "A", "B", "C", "D", "num")
下面的 if 语句需要列的名称: (也编辑为只排序一次)
for (i in seq_len(nrow(df))) {
row = sort(df[i,3:6])
if (df[i,1]==names(row)[4]) {
df$num[i] = row[3]/(row[3]+row[4])
} else {
df$num[i] = row[4]/(row[3]+row[4])
}
}
这样我得到了这个结果:
> df
first_name sec A B C D num
1 A 77 79 32 5 4 0.2882883
2 B 57 24 68 65 45 0.4887218
3 C 66 60 82 74 22 0.525641
我不确定如何使用 apply 执行此操作,是否在想这样的事情?尽管这不起作用,而且我不确定如何合并 if/else 条件:
df$num <- apply(df, 1, function(x) unlist(x[3:6][order(x[3:6])][3]/(x[3:6][order(x[3:6])][3]+x[3:6][order(x[3:6])][4])))
这里有一些硬编码(列索引)的小蛮力方法。
cols <- 3:6
sorted2 <- t(apply(df[,cols], 1, sort, decreasing = TRUE))[,1:2]
sorted2
# [,1] [,2]
# 1 79 32
# 2 68 65
# 3 82 74
df$num <- ifelse(df[,1] == names(df)[cols][max.col(df[,cols])],
sorted2[,2], sorted2[,1]) /
rowSums(sorted2)
df
# first_name sec A B C D num
# 1 A 77 79 32 5 4 0.2882883
# 2 B 57 24 68 65 45 0.4887218
# 3 C 66 60 82 74 22 0.5256410
备注:
- 我定义
cols
这样如果你改变列,你只需要改变一次。 - 您的代码倾向于比较
first_name
是否与列名匹配,并据此确定您是使用cols
列中的“最大值”还是“第二最大值”,以及然后将 that 数字除以两个最大数字的总和。为此,我计算sorted2
,它始终在第 1 列中具有最大值,在第 2 列中具有第二个最大值,从而使rowSums
和 max/2nd-max 访问 immediate/easy. - 我本可以使用
df$first_name
而不是df[,1]
,你的选择。
这应该比 for
循环或 sapply
工作得更好,因为它是完全矢量化的。