【问题标题】:R: selecting row values based on row rangeR:根据行范围选择行值
【发布时间】:2017-05-02 15:07:28
【问题描述】:

我有一个数据框 (df),其中包含 4 列值(V1 到 V4 列),我需要根据另外两列(最大和最小列)来选择这些值。我的目标是将 NA 分配给每行的 max 和 min 列设置的范围之外的值,并计算剩余值的平均值。

V1   V2   V3   V4   max  min
 1    3    6    8     7    5
23   30    5   17    30   16 

预期的输出是:

V1   V2   V3   V4   max  min  mean
NA   NA    6   NA     7    5     6
23   30   NA   17    30   16    35

到目前为止,我只能通过使用以下脚本来分配 NA 来做到这一点...

df$V1 <- ifelse(df$V1 > df$max | df$V1 < df$min, NA, df$V1)

df$V2 <- ifelse(df$V2 > df$max | df$V2 < df$min, NA, df$V2)

df$V3 <- ifelse(df$V3 > df$max | df$V3 < df$min, NA, df$V3)

df$V4 <- ifelse(df$V4 > df$max | df$V4 < df$min, NA, df$V4)

...然后以下计算平均值:

df$mean <- rowMeans(df[, 1:4], na.rm = TRUE)

问题是实际数据中的列数会远大于 4,而且这种方法似乎需要太多的重复。在 R 中有没有更好的方法来做到这一点?

我尝试使用data.table 对有效值进行子集化,然后使用apply 函数但没有成功:

df <- df[df[,1:4] <= df$max | df[,1:4] >= df$min, ]

apply(df[,1:4], 1, function(x) mean(x))

谢谢。

【问题讨论】:

  • melt

标签: r


【解决方案1】:

例如,您可以尝试以下方法,首先融合您的数据。

# getting your data:
df <- read.table(text="V1   V2   V3   V4   max  min
                        1    3    6    8     7    5
                       23   30    5   17    30   16", header=T)

# melting the data:
library(reshape2)
df2 <- melt(df, id.vars = c("max", "min"))
df2
max min variable value
1   7   5       V1     1
2  30  16       V1    23
3   7   5       V2     3
4  30  16       V2    30
5   7   5       V3     6
6  30  16       V3     5
7   7   5       V4     8
8  30  16       V4    17

# I create a new vector with NAs, but you could easily just overwrite the values:
df2$val <- with(df2, ifelse(value > max | value < min, NA, value))

# Cast the data into the old form again.
df3 <- dcast(df2, max + min ~ variable, value.var = "val")
# calculate the rowMeans:
df3$mean <- rowMeans(df3[, 3:6], na.rm = TRUE)

# Doing some cosmetics here to get the same column ordering. Chose your preferred way or rearranging the columns, if required at all.
df3 <- df3[, c(paste0("V", 1:4),"max", "min", "mean") ]
df3
  V1 V2 V3 V4 max min     mean
1 NA NA  6 NA   7   5  6.00000
2 23 30 NA 17  30  16 23.33333

请注意,唯一的区别是第二行的平均值较低。我不确定你是如何得到 35 的值的。

【讨论】:

  • 感谢您的回复。尽管使用melt 会起作用,但我认为大量列可能会导致问题(至少在创建长字符串时看起来很慢)。
【解决方案2】:

试试:

df <- read.table(header=TRUE, text="V1   V2   V3   V4   max  min
 1    3    6    8     7    5
23   30    5   17    30   16")

df.new<-apply(df[,1:4],2,function(x) ifelse(x>df[,5] | x<df[,6],NA,x))
df.new<-cbind(df.new,df[,5:6])
df.new$mean=rowMeans(df.new[1:4],na.rm=TRUE)
df.new

【讨论】:

  • 谢谢。这就是我一直在寻找的……当然,apply 按列!
【解决方案3】:

这是一个简单的解决方案,使用 for 循环填充 NA,rowMeans 计算每行的平均值。

# loop through rows and fill in NA for values outside of min/max
for(i in 1:nrow(df))
    is.na(df[i, 1:4]) <- df[i, 1:4] < df[i, "min"] | df[i, 1:4] > df[i, "max"]

# calculate mean of each row
df$mean <- rowMeans(df[, 1:4], na.rm=TRUE)

返回

df
  V1 V2 V3 V4 max min     mean
1 NA NA  6 NA   7   5  6.00000
2 23 30 NA 17  30  16 23.33333

【讨论】:

  • 感谢您的回复。是的,但是,我更喜欢尽可能避免 for 循环。
  • 请注意,所有*apply 函数实际上都是循环。 lapply 中的循环是在 c 中实现的,apply 中的循环实际上是作为 R for 循环实现的。您可以通过阅读apply 的代码自己看到这一点。 (在控制台中输入不带括号的 apply 并回车)。
猜你喜欢
  • 1970-01-01
  • 2017-01-11
  • 2020-02-10
  • 2017-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
相关资源
最近更新 更多