【问题标题】:Juila Dataframe rollup by groups (aka subtotals)Julia Dataframe 按组汇总(又名小计)
【发布时间】:2021-08-12 20:44:20
【问题描述】:

在 DataFrames.jl 中表达汇总聚合的简洁方式是什么?

示例数据集:

+---+----------+-----+---------+------+
| id|  date_col|group|     item|amount|
+---+----------+-----+---------+------+
|  1|2020-03-11|    A|BOO00OXXX|   1.0|
|  2|2020-03-11|    A|BOO00OXXY|   2.0|
|  3|2020-03-11|    B|BOO00OXXZ|  17.0|
|  4|2020-03-12|    B|BOO00OXXA|   9.0|
|  5|2020-03-12|    B|BOO00OXXB|   1.0|
|  6|2020-03-12|    B|BOO00OXXY|   5.0|
|  7|2020-03-13|    C|BOO00OXXY|   2.0|
|  8|2020-03-13|    C|BOO00OXXX|   1.0|
|  9|2020-03-13|    C|BOO00OXXY|   2.0|
+---+----------+-----+---------+------+

# desired output
+------+---------+
|group |total_amt|
+------+---------+
|ROLLUP|     40.0|
|    A |      3.0|
|    B |     32.0|
|    C |      5.0|
+------+---------+

我通常需要汇总一个数据集,有时用于共享报告,该报告通过小计和总计汇总某些列的值。这些在 Excel 中称为“汇总”或“小计”/“总计”。

在 Spark 中,这些是通过 rollupcube 聚合方便地生成的。上面的结果是通过以下 spark api 调用生成的。

如何使用 Julia DataFrames.jl 生成类似的表格?

// scala spark
df.rollup("group")
    .agg(sum("amount").as("total_amt"))
    .orderBy("group")
    .show()

+-----+---------+
|group|total_amt|
+-----+---------+
| null|     40.0|
|    A|      3.0|
|    B|     32.0|
|    C|      5.0|
+-----+---------+
// note the aggregated column label is null for the subtotal (aka rollup)

注意:我可以使用多个 julia groupby()combine() 操作生成结果,然后将结果合并或 vcat 到单个数据帧中。我需要并且想要一个简洁易读的成语。


编辑:添加一个特定的 julia 实现来说明为什么我想要更简洁的东西。

using DataFrames, Dates
df = DataFrame(id = [1,2,3,4,5,6,7,8,9]
    , date_col = Date.(["2020-03-11","2020-03-11","2020-03-11","2020-03-12","2020-03-12","2020-03-12","2020-03-13","2020-03-13","2020-03-13"])
    , group = ["A","A","B","B","B","B","C","C","C"]
    , amount = [1.0,2.0,17.0,9.0,1.0,5.0,2.0,1.0,2.0]
    )

# replicate the spark.rollup example
df1 = combine(groupby(_, :group), :amount => sum => :total_amt);
df2 = combine(df, :amount => sum => :total_amt);
df2[:, :group] = [missing];
df_result = sort(vcat(df1, df2, cols = :setequal), rev = true)

4×2 DataFrame
 Row │ group    total_amt
     │ String?  Float64
─────┼────────────────────
   1 │ missing       40.0
   2 │ C              5.0
   3 │ B             32.0
   4 │ A              3.0

添加@bkamins 答案的版本,坚持使用combine() 到目前为止,我认为我更喜欢这个答案,因为它保持了一点对称性,并且如果将其制成一个函数,则更容易看到参数的去向。

using Chain
@chain df begin
    groupby(:group)
    combine(:amount => sum => :total_amt)
    append!(insertcols!(combine(df, :amount => sum => :total_amt), :group => "ROLLUP"))
    sort(:total_amt, rev = true)
end

【问题讨论】:

    标签: dataframe julia dataframes.jl


    【解决方案1】:

    我会这样做:

    julia> using DataFrames, Chain
    
    julia> df = DataFrame(group=["A", "A", "B", "B", "C", "C"], amount=1:6)
    6×2 DataFrame
     Row │ group   amount
         │ String  Int64
    ─────┼────────────────
       1 │ A            1
       2 │ A            2
       3 │ B            3
       4 │ B            4
       5 │ C            5
       6 │ C            6
    
    julia> @chain df begin
           groupby(:group)
           combine(:amount => sum => :total_amount)
           push!(_, (missing, sum(_.total_amount)), promote=true)
           sort(:total_amount, rev=true)
           end
    4×2 DataFrame
     Row │ group    total_amount
         │ String?  Int64
    ─────┼───────────────────────
       1 │ missing            21
       2 │ C                  11
       3 │ B                   7
       4 │ A                   3
    

    这将是有效的,希望您会发现它可读。 正如@jling 评论的那样,我们没有内置汇总。

    【讨论】:

    • 谢谢@Bogumił,我担心这不会是单行的。我的目的是生产代码的可读性和可维护性。这是一个很好的例子,虽然我想保持组和汇总之间求和调用的对称性,即在两种情况下都使用组合。
    • 您可以在df 上使用combine 并使用vcatcols = :union 来组合使用两次。
    【解决方案2】:

    这是 DataFramesMeta.jl 的答案

    julia> using DataFramesMeta;
    
    julia> @chain df begin 
               groupby(:group)
               @combine :total_amount = sum(:amount)
               @aside df2 = @combine df :total_amount = sum(:amount)
               vcat(df2; cols = :union)
           end
    4×2 DataFrame
     Row │ group    total_amount 
         │ String?  Int64        
    ─────┼───────────────────────
       1 │ A                   3
       2 │ B                   7
       3 │ C                  11
       4 │ missing            21
    

    【讨论】:

    • 我喜欢这里的可读性,同事维护这样的代码更容易。添加另一个依赖项是一个值得考虑的小负担。
    【解决方案3】:
    julia> df
    5×2 DataFrame
     Row │ g      amt   
         │ Int64  Int64 
    ─────┼──────────────
       1 │     0      2
       2 │     1      1
       3 │     1      1
       4 │     0      1
       5 │     1      1
    
    julia> combine(groupby(df, :g), :amt => sum => :total_amt)
    2×2 DataFrame
     Row │ g      total_amt 
         │ Int64  Int64        
    ─────┼─────────────────────
       1 │     0             3
       2 │     1             3
    
    #alternative do-block syntax:
    
    julia> combine(groupby(df, :g)) do sub_df
               (total_amt = sum(sub_df.amt),)
           end
    2×2 DataFrame
     Row │ g      total_amt
         │ Int64  Int64     
    ─────┼──────────────────
       1 │     0          3
       2 │     1          3
    

    这或多或少是你想要的吗?顺便说一句,相关文档:https://dataframes.juliadata.org/stable/man/split_apply_combine/

    我觉得我们需要一个感觉迭代来解决你可能想在 Spark 中做的所有事情,所以很难来回做这些事情。

    【讨论】:

    • 感谢您的快速响应,我喜欢 jl 社区。要求要求另一行包含第 0 组和第 1 组的总数(在本例中为 6)。
    • @Merlin 新行的g 是什么?问题是g 列的元素类型为Int64,“汇总”不是Int64。因此,您需要为此分配一个全新的列。恕我直言,sum(newdf.total_amt) 很简单
    • @jiling 使用我提供的数据框示例,或者注意 spark rollup 命令为汇总组返回 NULL。您提供的解决方案需要创建两个聚合数据框,然后将它们组合以产生所需的结果。我正在寻找一个简洁表达式来生成结果。
    • @Merlin ROLLUP 看起来像是 spark 内部的一些特殊东西,在 Julia 的 DataFrame 中没有特殊的“ROLLUP”对象。当前解决方案有哪些不简洁的地方?
    猜你喜欢
    • 2014-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-31
    • 1970-01-01
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多