【问题标题】:How to count entries of the same value for each individual row of a data frame?如何为数据框的每一行计算相同值的条目?
【发布时间】:2020-01-22 15:12:24
【问题描述】:

假设我有一个包含订单日期、用户 ID 和商品 ID 的大型数据框。

   order_id order_date user_id item_id
1         1 2016-06-22   30822     643
2         2 2016-06-22   30822     337
3         3 2016-06-22   30823     270
4         4 2016-06-22   30823     142
5         5 2016-06-22   30823     561
6         6 2016-06-22   30823     561
7         7 2016-06-22   30823      72
8         8 2016-06-22   30823     106
9         9 2016-06-22   30823     195
10       10 2016-06-22   30823     195

我想要做的是计算同一用户在同一天下了多少订单,以及同一用户下了多少同一商品的订单,并在第四列和第五列中显示这两个值。所以结果应该是这样的:

   order_id order_date user_id item_id same_day same_item
1         1 2016-06-22   30822     643        2         1
2         2 2016-06-22   30822     337        2         1
3         3 2016-06-22   30823     270        8         1
4         4 2016-06-22   30823     142        8         1
5         5 2016-06-22   30823     561        8         2
6         6 2016-06-22   30823     561        8         2
7         7 2016-06-22   30823      72        8         1
8         8 2016-06-22   30823     106        8         1
9         9 2016-06-22   30823     195        8         2
10       10 2016-06-22   30823     195        8         2

或者,显示项目是否多次订购的二进制变量也可以完成这项工作。我知道可以使用sum() 轻松计算每一行:

> sum(df$order_date=="2016-06-22" & df$user_id==30823)
[1] 8
> sum(df$item_id==561 & df$user_id==30823)
[1] 2

但我宁愿让它自动运行,而不是手动运行数千个条目。我的想法严重不足,非常感谢您的帮助!

【问题讨论】:

    标签: r loops dataframe count automation


    【解决方案1】:
    library(data.table)
    setDT(df)
    
    df[, same_day := .N, by = .(order_date, user_id)]
    df[, same_item := .N, by = .(order_date, user_id, item_id)]
    
    df
    #     order_id order_date user_id item_id same_day same_item
    #  1:        1 2016-06-22   30822     643        2         1
    #  2:        2 2016-06-22   30822     337        2         1
    #  3:        3 2016-06-22   30823     270        8         1
    #  4:        4 2016-06-22   30823     142        8         1
    #  5:        5 2016-06-22   30823     561        8         2
    #  6:        6 2016-06-22   30823     561        8         2
    #  7:        7 2016-06-22   30823      72        8         1
    #  8:        8 2016-06-22   30823     106        8         1
    #  9:        9 2016-06-22   30823     195        8         2
    # 10:       10 2016-06-22   30823     195        8         2
    

    或者使用 dplyr

    library(dplyr)
    
    df <- 
     df %>% 
       add_count(order_date, user_id, name = 'same_day') %>% 
       add_count(order_date, user_id, item_id, name = 'same_item')
    
    df
    # # A tibble: 10 x 6
    #    order_id order_date user_id item_id same_day same_item
    #       <int> <chr>        <int>   <int>    <int>     <int>
    #  1        1 2016-06-22   30822     643        2         1
    #  2        2 2016-06-22   30822     337        2         1
    #  3        3 2016-06-22   30823     270        8         1
    #  4        4 2016-06-22   30823     142        8         1
    #  5        5 2016-06-22   30823     561        8         2
    #  6        6 2016-06-22   30823     561        8         2
    #  7        7 2016-06-22   30823      72        8         1
    #  8        8 2016-06-22   30823     106        8         1
    #  9        9 2016-06-22   30823     195        8         2
    # 10       10 2016-06-22   30823     195        8         2
    

    【讨论】:

    • 非常感谢您的帮助!我可以使第一种方法工作,即使在非常大的数据集上,也没有任何问题。 dplyr 方法似乎不会改变数据框本身,而只是输出解决方案。这很好,但不是我打算做的。
    • 是的 data.table 在分组方面非常有效。对于dplyr 解决方案,您必须重新分配给df(通过在df %&gt;% 上方的行中添加df &lt;-),但我认为这应该比data.table 方法更慢/更占用内存。
    【解决方案2】:

    这是使用 ave 的基本 R 解决方案

    df$same_day <- with(df,ave(item_id,order_date,user_id,FUN = length))
    df$same_item <- with(df,ave(item_id,order_date,user_id,item_id,FUN = length))
    

    这样

    > df
       order_id order_date user_id item_id same_day same_item
    1         1 2016-06-22   30822     643        2         1
    2         2 2016-06-22   30822     337        2         1
    3         3 2016-06-22   30823     270        8         1
    4         4 2016-06-22   30823     142        8         1
    5         5 2016-06-22   30823     561        8         2
    6         6 2016-06-22   30823     561        8         2
    7         7 2016-06-22   30823      72        8         1
    8         8 2016-06-22   30823     106        8         1
    9         9 2016-06-22   30823     195        8         2
    10       10 2016-06-22   30823     195        8         2
    

    【讨论】:

    • 非常感谢您的回答!这是一个有效的简单解决方案!不幸的是,对于我的数据框的大小(或者对于我的 PC,无论如何 R 在将其应用于整个数据集后崩溃),它似乎太占用内存了。
    • @Zwiebak 好吧,也许这是使用基础 R 的限制之一......
    • @Zwiebak 您仍然可以找到它的基本 r 解决方案。 dplyr 主要是用 R + 一些 C 编写的
    【解决方案3】:

    两个dplyr 接近。

    首先是创建组并添加具有相应观察次数的变量:

    library(dplyr)
    
    df %>%
      group_by(user_id) %>%
      mutate(same_day = length(order_date)) %>%
      group_by(user_id, item_id) %>%
      mutate(same_item = length(item_id))
    #> # A tibble: 10 x 6
    #> # Groups:   user_id, item_id [8]
    #>    order_id order_date user_id item_id same_day same_item
    #>       <int> <fct>        <int>   <int>    <int>     <int>
    #>  1        1 2016-06-22   30822     643        2         1
    #>  2        2 2016-06-22   30822     337        2         1
    #>  3        3 2016-06-22   30823     270        8         1
    #>  4        4 2016-06-22   30823     142        8         1
    #>  5        5 2016-06-22   30823     561        8         2
    #>  6        6 2016-06-22   30823     561        8         2
    #>  7        7 2016-06-22   30823      72        8         1
    #>  8        8 2016-06-22   30823     106        8         1
    #>  9        9 2016-06-22   30823     195        8         2
    #> 10       10 2016-06-22   30823     195        8         2
    

    第二个是创建两个汇总表,same_daysame_item 各一个。汇总表只有与汇总值一样多的行,然后通过左连接重复。这类似于 SQL 子查询,如果您熟悉的话。

    df %>%
      left_join(
        df %>%
          count(user_id, name = "same_day"),
        by = "user_id"
      ) %>%
      left_join(
        df %>%
          count(user_id, item_id, name = "same_item"),
        by = c("user_id", "item_id")
      )
    # same output
    

    【讨论】:

    • 感谢您的评论!不幸的是,我无法使您的任何方法奏效。两次我都收到一条错误消息,没有找到“dat”,即使我之前加载了 dplyr……我忘记了什么吗?
    • dat 在这个答案中只是数据名称的占位符。如果您的数据是这样命名的,您可以将其替换为 df
    • 是的,我只是没有注意到您将数据框称为什么并给了它一个不同的默认名称。我会编辑以匹配
    【解决方案4】:

    你可以使用 dplyr 包

    您可以使用dplyr 来达到预期的效果:

    library(dplyr)
    data <- data %>%
      group_by(order_date, user_id) %>% mutate(same_day = n()) %>%
      group_by(item_id, user_id) %>% mutate(same_item = n()) %>% 
      ungroup()
    

    结果:

    # A tibble: 10 x 6
       order_id order_date user_id item_id same_day same_item
          <int> <fct>        <int>   <int>    <int>     <int>
     1        1 2016-06-22   30822     643        2         1
     2        2 2016-06-22   30822     337        2         1
     3        3 2016-06-22   30823     270        8         1
     4        4 2016-06-22   30823     142        8         1
     5        5 2016-06-22   30823     561        8         2
     6        6 2016-06-22   30823     561        8         2
     7        7 2016-06-22   30823      72        8         1
     8        8 2016-06-22   30823     106        8         1
     9        9 2016-06-22   30823     195        8         2
    10       10 2016-06-22   30823     195        8         2
    

    希望这会有所帮助。

    【讨论】:

    • 感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-17
    • 2017-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    相关资源
    最近更新 更多