【问题标题】:Subset dataframe based on Values in second dataframe基于第二个数据帧中的值的子集数据帧
【发布时间】:2018-08-27 23:43:53
【问题描述】:

我有一个数据框df,它有两列:

> head(df1[,c(10,11)])
       ColA      ColB
1        12        20
2         7         5
3        32        38
4        37        46
5        15        15
6         4         4

我有第二个数据框,也有 2 列名称匹配。相反,只有两个数字,例如:

> head(df2)
       ColA      ColB
1        50        30

我想根据来自 df2 的相应列中的值对来自 df1 的值进行子集化。手动执行此操作如下所示:

colA_vector <- df1[df1$colA < 50,]
colB_vector <- df1[df1$ColB < 30,]

如何以更通用的方式执行此操作?我不想硬编码任何东西。列名“ColA”或“ColB”可以是任何名称(因此需要这些列名的解决方案实际上不会起作用)。

谢谢。

【问题讨论】:

  • df2$ColA代替50
  • 我不想硬编码任何东西。 ColA 可能会因其他分析而发生变化。
  • 拜托,你能edit你的Q并显示预期的结果吗?我问是因为colA_vector 不是向量而是数据框。谢谢。

标签: r


【解决方案1】:

在基础R 中我们可以这样做:

nms <- intersect(names(df1), names(df2))
df1[do.call(`&`, Map(`<`, df1[nms], df2[nms])),]
#   ColA ColB
# 1   12   20
# 2    7    5
# 5   15   15
# 6    4    4

如果两个 data.frame 具有相同的列顺序和相同的名称,则只需 df1[do.call('&amp;', Map('&lt;', df1, df2)),]

不过,使用包 fuzzyjoin 可能更具可读性:

library(fuzzy_join)
fuzzy_semi_join(df1, df2, match_fun = `<`)
#   ColA ColB
# 1   12   20
# 2    7    5
# 5   15   15
# 6    4    4

数据

df1 <- read.table(text="
ColA      ColB
1        12        20
2         7         5
3        32        38
4        37        46
5        15        15
6         4         4",h=T,strin=F)

df2 <- read.table(text="ColA      ColB
1        50        30",h=T,strin=F)

【讨论】:

  • 完美。谢谢。
【解决方案2】:

如果我们想重复执行相同的任务,请创建一个函数

f1 <- function(dat1, dat2, colName) {
        dat1[dat1[[colName]] < dat2[[colName]],]
  }

f1(df1, df2, "ColA")
#  ColA ColB
#1   12   20
#2    7    5
#3   32   38
#4   37   46
#5   15   15
#6    4    4

f1(df1, df2, "ColB")
#  ColA ColB
#1   12   20
#2    7    5
#5   15   15
#6    4    4

数据

df1 <- structure(list(ColA = c(12L, 7L, 32L, 37L, 15L, 4L), ColB = c(20L, 
5L, 38L, 46L, 15L, 4L)), class = "data.frame", row.names = c(NA, 
-6L))

df2 <- structure(list(ColA = 50L, ColB = 30L), 
     class = "data.frame", row.names = "1")

【讨论】:

    【解决方案3】:

    使用dplyr

    df1 %>%
      filter(df1[,1] < df2[,1])
    
      ColA ColB
    1   12   20
    2    7    5
    3   32   38
    4   37   46
    5   15   15
    6    4    4
    
    df1 %>%
      filter(df1[,2] < df2[,2])
    
      ColA ColB
    1   12   20
    2    7    5
    3   15   15
    4    4    4
    

    同时基于两列的子集:

    df1 %>%
      filter(df1[,1] < df2[,1] & df1[,2] < df2[,2])
    
      ColA ColB
    1   12   20
    2    7    5
    3   15   15
    4    4    4
    

    【讨论】:

      【解决方案4】:

      如果您不想使用 fuzzyjoin 加入包或制作自己的函数,则可以重复第二个数据帧。

      df1 <- data.frame("ColA" = c(12, 7, 32),
                   "ColB" = c(20, 5, 38))
      df2 <- data.frame("ColA" = 50,
                    "ColB" = 30)
      
      n <- nrow(df1)
      df2_new <- do.call("rbind", replicate(n, df2, simplify = FALSE))
      df1_which <- as.data.frame(df1 < df2_new)
      
      colA_vector <- df1[df1_which$ColA, "ColA"]
      colB_vector <- df1[df1_which$ColB, "ColB"]
      

      【讨论】:

        【解决方案5】:

        您可以尝试tidyverse 功能。结果是过滤后的 data.frames 的列表。

        foo <- function(x, y, ColA, ColB){
          require(tidyverse)
          var1 <- quo_name(ColA)
          var2 <- quo_name(ColB)
          x %>%
          select(a=!!var1, b=!!var2) %>% 
          mutate(colA_vector= a < y[[ColA]]) %>% 
          mutate(colB_vector= b < y[[ColB]]) %>% 
          gather(k, v, -a, -b) %>% 
          filter(v) %>%
          split(.$k) %>% 
          map(~select(.,-v,-k))
        }
        foo(df1, df2, "ColA", "ColB")
        $colA_vector
           a  b
        1 12 20
        2  7  5
        3 32 38
        4 37 46
        5 15 15
        6  4  4
        
        $colB_vector
            a  b
        7  12 20
        8   7  5
        9  15 15
        10  4  4
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-07-08
          • 2012-08-13
          • 2018-06-07
          • 2018-11-12
          • 1970-01-01
          • 2020-06-22
          • 2022-07-21
          相关资源
          最近更新 更多