【问题标题】:compare multiple columns and create count of matches比较多列并创建匹配计数
【发布时间】:2019-01-08 04:29:35
【问题描述】:

我有一个数据集,其中包含受访者的朋友和恶霸的 ID 号。

我想查看每一行的所有友谊提名和所有恶霸提名,并计算他们提名的人数。任何帮助都会很棒!

有数据:

ID  friend_1  friend_2  friend_3  bully_1  bully_2
1          4        12         7       12       15
2          8         6         7       18       20
3          9        18         1        2        1
4         15         7         2        7       13 
5          1        17         9       17        1
6          9        19        20       14       12
7         19        12        20        9       12
8          7         1        16        2       15 
9          1        10        12        1        7
10         7        11         9       11        7

想要数据:

ID  friend_1  friend_2  friend_3  bully_1  bully_2  num_both
1          4        12         7       12       15         1
2          8         6         7       18       20         0
3          9        18         1        2        1         1
4         15         7         2        7       13         1
5          1        17         9       17        1         2
6          9        19        20       14       12         0
7         19        12        20        9       12         1
8          7         1        16        2       15         0
9          1        10        12        1        7         1
10         7        11         9       11        7         2

【问题讨论】:

    标签: r loops


    【解决方案1】:

    假设值在朋友/欺凌群体中是唯一的,一个简单的方法是:

    apply(df[,-1], 1, function (x) sum(table(x) > 1)) 
    [1] 1 0 1 1 2 0 1 0 1 2
    

    【讨论】:

      【解决方案2】:

      我们可以使用apply row-wise 并找出在friendbully 列中都存在的共同朋友的数量

      df$num_both <- apply(df, 1, function(x) 
            length(intersect(x[grep("friend", names(df))], x[grep("bully", names(df))])))
      
      
      #   ID friend_1 friend_2 friend_3 bully_1 bully_2 num_both
      #1   1        4       12        7      12      15        1
      #2   2        8        6        7      18      20        0
      #3   3        9       18        1       2       1        1
      #4   4       15        7        2       7      13        1
      #5   5        1       17        9      17       1        2
      #6   6        9       19       20      14      12        0
      #7   7       19       12       20       9      12        1
      #8   8        7        1       16       2      15        0
      #9   9        1       10       12       1       7        1
      #10 10        7       11        9      11       7        2
      

      或者如果你不是apply的忠实粉丝,你可以用同样的逻辑使用sapply

      friend_cols <- grep("friend", names(df))
      bully_cols <- grep("bully", names(df))
      
      sapply(seq_len(nrow(df)), function(i) 
       length(intersect(df[i, friend_cols, drop = TRUE], df[i, bully_cols, drop = TRUE])))
      
      #[1] 1 0 1 1 2 0 1 0 1 2
      

      编辑

      如果有一些 NA 值并且我们想排除它们,我们可以使用 is.nasum

      apply(df, 1, function(x) sum(!is.na(intersect(x[friend_cols], x[bully_cols]))))
      

      【讨论】:

      • 嘿,谢谢!我认为应用版本几乎就在那里,但它正在计算 NA 的行数,其中一些观察的提名比其他观察少。知道如何确保在计算匹配项时忽略 NA 吗?
      • 我们可以使用sumis.na 忽略NA 匹配。我已经更新了答案。
      【解决方案3】:

      您可以尝试将每个bully 列与friends 列进行比较,然后采用联合计算匹配矩阵。要获得您的num_both,您只需rowSum 这个匹配矩阵:

      bully_cols <- grep("bully", names(df))
      friend_cols <- grep("friend", names(df))
      df$num_both <- rowSums(Reduce("|", lapply(df[,bully_cols], function(x, compare) compare == x, compare = df[,friend_cols])))
      

      lapply 计算每个欺负列的匹配项,然后Reduce 将它们组合成一个矩阵以对行求和。

      #   ID friend_1 friend_2 friend_3 bully_1 bully_2 num_both
      #1   1        4       12        7      12      15        1
      #2   2        8        6        7      18      20        0
      #3   3        9       18        1       2       1        1
      #4   4       15        7        2       7      13        1
      #5   5        1       17        9      17       1        2
      #6   6        9       19       20      14      12        0
      #7   7       19       12       20       9      12        1
      #8   8        7        1       16       2      15        0
      #9   9        1       10       12       1       7        1
      #10 10        7       11        9      11       7        2
      

      【讨论】:

        【解决方案4】:

        这是来自data.table 的基于melt 的方法。我们将melt根据列名中的patterns(以friendbully开头),按'ID'分组,得到intersecting元素中的length的long格式数据集列“value1”、“value2”并连接on“ID”

        library(data.table)
        setDT(df1)[melt(df1, measure = patterns('^friend', '^bully'))[,
           .(num_both = length(intersect(value1, value2))), ID], on = .(ID)]
        #    ID friend_1 friend_2 friend_3 bully_1 bully_2 num_both
        # 1:  1        4       12        7      12      15        1
        # 2:  2        8        6        7      18      20        0
        # 3:  3        9       18        1       2       1        1
        # 4:  4       15        7        2       7      13        1
        # 5:  5        1       17        9      17       1        2
        # 6:  6        9       19       20      14      12        0
        # 7:  7       19       12       20       9      12        1
        # 8:  8        7        1       16       2      15        0
        # 9:  9        1       10       12       1       7        1
        #10: 10        7       11        9      11       7        2
        

        或者使用tidyverse by gathering 成'long' 格式,按'ID'分组,summarise 加上lengthintersecting 元素的'value' 基于'friend'的出现' 或 'bully' 在 'key' 列和right_join 与原始数据集

        library(tidyverse)
        df1 %>% 
           gather(key, value, -ID) %>% 
           group_by(ID) %>% 
           summarise(num_both = length(intersect(value[str_detect(key, 'friend')], 
                                 value[str_detect(key, 'bully')]))) %>% 
           right_join(df1)
        # A tibble: 10 x 7
        #      ID num_both friend_1 friend_2 friend_3 bully_1 bully_2
        #   <int>    <int>    <int>    <int>    <int>   <int>   <int>
        # 1     1        1        4       12        7      12      15
        # 2     2        0        8        6        7      18      20
        # 3     3        1        9       18        1       2       1
        # 4     4        1       15        7        2       7      13
        # 5     5        2        1       17        9      17       1
        # 6     6        0        9       19       20      14      12
        # 7     7        1       19       12       20       9      12
        # 8     8        0        7        1       16       2      15
        # 9     9        1        1       10       12       1       7
        #10    10        2        7       11        9      11       7
        

        或者通过pmap循环遍历行的另一种方法

        df1 %>% 
             mutate(num_both = pmap(.[-1], ~ c(...) %>%
                                         {length(intersect(.[1:3], .[4:5]))}))
        

        数据

        df1 <- structure(list(ID = 1:10, friend_1 = c(4L, 8L, 9L, 15L, 1L, 9L, 
        19L, 7L, 1L, 7L), friend_2 = c(12L, 6L, 18L, 7L, 17L, 19L, 12L, 
        1L, 10L, 11L), friend_3 = c(7L, 7L, 1L, 2L, 9L, 20L, 20L, 16L, 
        12L, 9L), bully_1 = c(12L, 18L, 2L, 7L, 17L, 14L, 9L, 2L, 1L, 
        11L), bully_2 = c(15L, 20L, 1L, 13L, 1L, 12L, 12L, 15L, 7L, 7L
        )), class = "data.frame", row.names = c(NA, -10L))
        

        【讨论】:

          猜你喜欢
          • 2022-11-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-18
          • 2015-12-23
          • 2015-03-08
          • 1970-01-01
          相关资源
          最近更新 更多