R:根据行范围选择行值

时间:2017-05-02 15:07:28

标签: r

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

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

到目前为止,我只能通过使用以下脚本来分配NAs ...

  

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))

谢谢。

3 个答案:

答案 0 :(得分:2)

例如,您可以尝试以下方法,首先解压缩数据。

# 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。

答案 1 :(得分:1)

尝试:

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

答案 2 :(得分:1)

这是一个简单的解决方案,其中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