【问题标题】:Applying calculation per groups within R dataframe在 R 数据框中按组应用计算
【发布时间】:2012-06-20 08:17:16
【问题描述】:

我有这样的数据:

object category country
495647 1        RUS  
477462 2        GER  
431567 3        USA  
449136 1        RUS  
367260 1        USA  
495649 1        RUS  
477461 2        GER  
431562 3        USA  
449133 2        RUS  
367264 2        USA  
...

其中一个对象出现在不同的(category, country) 对中,并且国家/地区共享一个类别列表。

我想在其中添加另一列,这将是每个国家/地区的类别权重 - 出现在某个类别的类别中的对象数量,标准化为一个国家/地区内的总和为 1(仅对唯一 @ 987654323@对)。

我可以这样做:

aggregate(df$object, list(df$category, df$country), length)

然后从那里计算权重,但是直接在原始数据上执行此操作的更有效和优雅的方法是什么。

所需的示例输出:

object category country weight
495647 1        RUS     .75
477462 2        GER     .5 
431567 3        USA     .5 
449136 1        RUS     .75
367260 1        USA     .25
495649 1        RUS     .75
477461 3        GER     .5
431562 3        USA     .5
449133 2        RUS     .25
367264 2        USA     .25
...

对于唯一的(category, country) 对,以上内容在国家/地区内总计为一个。

【问题讨论】:

  • 可能属于stackoverflow
  • 一些预期的输出数据会很好;例如,澄清第 1 行和第 4 行的结果是否都应为 50% 或都应为 100%。您直接在原始数据上要求按国家/地区划分的类别权重,这似乎暗示后者?但在 iiuc 国家/地区内,它的总和不会为 1。
  • 感谢 Matthew,您说的很对 - 第 1 行和第 4 行应该是 100%(或 1)。权重在一个国家/地区内应总计为 1,仅考虑唯一(类别、国家/地区)对 - 我正在编辑问题。

标签: r aggregation data.table


【解决方案1】:

特别考虑到最后一句话:“什么是直接在原始数据上执行此操作的更有效和更优雅的方式。”恰好data.table为此提供了一个新功能。

install.packages("data.table", repos="http://R-Forge.R-project.org")
# Needs version 1.8.1 from R-Forge.  Soon to be released to CRAN.

你的数据在DT

> DT[, countcat:=.N, by=list(country,category)]     # add 'countcat' column
    category country countcat
 1:        1     RUS        3
 2:        2     GER        1
 3:        3     USA        2
 4:        1     RUS        3
 5:        1     USA        1
 6:        1     RUS        3
 7:        3     GER        1
 8:        3     USA        2
 9:        2     RUS        1
10:        2     USA        1

> DT[, weight:=countcat/.N, by=country]     # add 'weight' column
    category country countcat weight
 1:        1     RUS        3   0.75
 2:        2     GER        1   0.50
 3:        3     USA        2   0.50
 4:        1     RUS        3   0.75
 5:        1     USA        1   0.25
 6:        1     RUS        3   0.75
 7:        3     GER        1   0.50
 8:        3     USA        2   0.50
 9:        2     RUS        1   0.25
10:        2     USA        1   0.25

:= 通过引用数据添加一列,是一个“旧”功能。新功能是它现在可以按组工作。 .N 是一个符号,用于保存每组中的行数。

这些操作是内存高效的,应该扩展到大数据;例如,1e81e9 行。

如果您不希望包含中间列 countcat,请稍后将其删除。同样,这是一种高效的操作,无论表的大小如何(通过在内部移动指针)都可以立即工作。

> DT[,countcat:=NULL]     # remove 'countcat' column
    category country weight
 1:        1     RUS   0.75
 2:        2     GER   0.50
 3:        3     USA   0.50
 4:        1     RUS   0.75
 5:        1     USA   0.25
 6:        1     RUS   0.75
 7:        3     GER   0.50
 8:        3     USA   0.50
 9:        2     RUS   0.25
10:        2     USA   0.25
> 

【讨论】:

  • 确实是优雅高效的解决方案,谢谢!注意:我不熟悉 data.table 包,发现 1./ R-project.org 存储库需要 R > 2.15 和 2./ DT 必须是 data.table - 所以我不得不做 DT = data.table(DT)在我的原始数据框上。
  • 作为一个不常使用 data.table 的用户,我有时会对完成事情的不同方式感到困惑,而且我经常不得不查找事情。是否有计划构建更全面的文档来取代目前构成小插图的常见问题解答?
  • @ilprincipe 我知道你的意思并完全同意。虽然没有计划,因为我们不知道它应该是什么样子!你喜欢什么?一个有常见用例的wiki会好吗?您可以提供 what 所需的任何帮助以及 如何 将不胜感激。如果您可以草拟或开始您想要的格式,我会很乐意填写。现有 data.table wiki 上的新页面?别的地方?维基格式或可重现的东西?我们需要这方面的帮助和指导。即使是您看到的常用习语列表也是一个很好的开始。
  • @nikola 抱歉,我应该向您提及这一点。做得很好。顺便说一句,as.data.table 的转换速度通常比 data.table 快,但我们会对其进行更改,以便您无需知道这一点以方便您。或者首先从data.table 开始。
  • 我猜想扩展 Wiki 格式是可取的。作为第一步,它可以是一个列出所有习语的唯一地址和一个或多或少完整的常见用例列表。一个适当的小插图/介绍应该更侧重于介绍 data.table 及其本身的用法,并在较小程度上关注其与数据帧和计算速度的比较。我知道速度是一个卖点,但应该更多地分开。无论如何,大多数用户很快就知道了这一点。我觉得我对 data.table 还不够熟悉,还没有做出太多贡献,但我会多考虑一下。
【解决方案2】:

其实我前段时间问过a similar question。 data.table 非常适合这一点,特别是现在实现了 := by group,并且不再需要自连接 - 如上所示。 base R 的最佳解决方案是ave()。也可以使用tapply()

这类似于上面的解决方案,使用ave()。但是,我强烈建议您查看 data.table。

df$count <- ave(x = df$object, df$country, df$category, FUN = length)
df$weight <- ave(x = df$count, df$country, FUN = function(x) x/length(x))

【讨论】:

    【解决方案3】:

    我没有看到一种可读的方式来在一行中做到这一点。但它可以非常紧凑。

    # Use table to get the counts.
    counts <- table(df[,2:3])
    # Normalize the table
    weights <- t(t(counts)/colSums(counts))
    # Use 'matrix' selection by names.
    df$weight <- weights[as.matrix(df[,2:3])]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-04
      • 1970-01-01
      • 1970-01-01
      • 2021-06-16
      • 1970-01-01
      • 2014-10-07
      • 1970-01-01
      相关资源
      最近更新 更多