【问题标题】:Separate transaction rows of bundled products into multiple rows of individual product将捆绑产品的交易行分成多行单独的产品
【发布时间】:2018-05-09 10:41:06
【问题描述】:

假设我有一个销售交易表,其中一些是单独的 SKU,一些是捆绑的 SKU。

Date, Product, Qty
1 Jan 2017, A, 10
2 Jan 2017, Bundle X, 5
3 Jan 2017, B, 10
4 Jan 2017, Bundle Y, 5

还有一个包含 Bundle-component 组合的单独表格:

ParentSKU, ComponentSKU, Quantity
Bundle X, A, 3
Bundle X, B, 5
Bundle X, C, 10
Bundle Y, P, 5
Bundle Y, Q, 7
Bundle Y, R, 12
Bundle Y, S, 3

如何定义一个函数以应用于整个销售交易表(或使用 for 循环),以便最终产品将带有捆绑 SKU 的行分解为带有 SKU 的多行?最终结果应如下所示:

Date, Product, Qty
1 Jan 2017, A, 10
2 Jan 2017, A, 15
2 Jan 2017, B, 25
2 Jan 2017, C, 50
3 Jan 2017, B, 10
4 Jan 2017, P, 25
4 Jan 2017, Q, 35
4 Jan 2017, R, 60
4 Jan 2017, S, 15

谢谢!

【问题讨论】:

    标签: python pandas numpy dataframe


    【解决方案1】:

    这是使用numpyitertools 的一种方式。

    设置

    import pandas as pd, numpy as np
    from itertools import chain
    
    # SETUP
    
    df1 = pd.DataFrame({'Date': ['Jan 2017', 'Jan 2017', 'Jan 2017', 'Jan 2017'],
                        'Product': ['A', 'Bundle X', 'B', 'Bundle Y'],
                        'Qty': [10 , 5, 10, 5]})
    
    df2 = pd.DataFrame({'ParentSKU': ['Bundle X', 'Bundle X' ,'Bundle X', 'Bundle Y',
                                      'Bundle Y', 'Bundle Y', 'Bundle Y'],
                        'ComponentSKU': ['A', 'B', 'C', 'P', 'Q', 'R', 'S'],
                        'Quantity': [3, 5, 10, 5, 7, 12, 3]})
    

    解决方案

    # Perform groupby on bundles
    bundles = df2.groupby('ParentSKU')['ComponentSKU'].apply(list)
    bundles_q = df2.groupby('ParentSKU')['Quantity'].apply(list)
    
    # Map bundles to df1
    df1['Product_Decomposed'] = df1['Product'].map(bundles).fillna(df1['Product'].apply(list))
    df1['Quantity_Decomposed'] = df1.apply(lambda x: [x['Qty']*i for i in bundles_q.get(x['Product'], [1])], axis=1)
    
    # Get lengths of each bundle
    lens = list(map(len, df1['Product_Decomposed']))
    
    # Create dataframe by repeating and chaining data
    res = pd.DataFrame({'Date': np.repeat(df1['Date'], lens),
                        'Product': list(chain.from_iterable(df1['Product_Decomposed'])),
                        'Qty': list(chain.from_iterable(df1['Quantity_Decomposed']))})
    

    结果

    print(res)
    
           Date Product  Qty
    0  Jan 2017       A   10
    1  Jan 2017       A   15
    1  Jan 2017       B   25
    1  Jan 2017       C   50
    2  Jan 2017       B   10
    3  Jan 2017       P   25
    3  Jan 2017       Q   35
    3  Jan 2017       R   60
    3  Jan 2017       S   15
    

    【讨论】:

    • 谢谢!但是有没有办法让我只使用 numpy 和 pandas?
    • 如果您使用numpy / pandas,您应该绝对itertools开放。前 2 个是第 3 方库,而 itertools 是标准库的一部分(每次安装都附带)。您在使用itertools 时遇到了具体问题吗?
    • 不是真的,我只是还没学过itertools。谢谢!我去看看。
    • 我在计算 df1['Quantity_decomposed'] 时遇到此错误。 “无法将输入数组从形状 (3) 广播到形状 (11)”。我想我遇到了这个页面中提到的问题,但我不知道如何解决它? stackoverflow.com/questions/43977463/…
    • 我认为形状 (11) 是指我的实际 df1 数据框的形状(它包含 11 列)。但我不确定“形状 (3)”是从哪里来的?
    【解决方案2】:

    一种方法是使用merge(使用 jpp 对 df1 和 df2 的便捷设置):

    # Split df1 into the ones we need to unbundle
    by_bundling = dict(list(df1.groupby(df1.Product.str.startswith("Bundle"))))
    
    # Select the ones we want to unbundle, and make the index a column
    unbundled = by_bundling[True].reset_index()
    
    # Merge this with our second table
    unbundled = unbundled.merge(df2, left_on="Product", right_on="ParentSKU")
    
    # Multiply the quantities
    unbundled["Qty"] *= unbundled["Quantity"]
    
    # Reduce to the columns of interest and rename
    unbundled = unbundled.set_index("index")[["Date", "ComponentSKU", "Qty"]]
    unbundled = unbundled.rename(columns={"ComponentSKU": "Product"})
    
    # Recombine and sort
    final = pd.concat([by_bundling[False], unbundled]).sort_index()
    

    这给了我

    In [57]: final
    Out[57]: 
           Date Product  Qty
    0  Jan 2017       A   10
    1  Jan 2017       A   15
    1  Jan 2017       B   25
    1  Jan 2017       C   50
    2  Jan 2017       B   10
    3  Jan 2017       P   25
    3  Jan 2017       Q   35
    3  Jan 2017       R   60
    3  Jan 2017       S   15
    

    这里唯一有趣的是合并:

    In [59]: unbundled.merge(df2, left_on="Product", right_on="ParentSKU")
    Out[59]: 
       index      Date   Product  Qty ComponentSKU ParentSKU  Quantity
    0      1  Jan 2017  Bundle X    5            A  Bundle X         3
    1      1  Jan 2017  Bundle X    5            B  Bundle X         5
    2      1  Jan 2017  Bundle X    5            C  Bundle X        10
    3      3  Jan 2017  Bundle Y    5            P  Bundle Y         5
    4      3  Jan 2017  Bundle Y    5            Q  Bundle Y         7
    5      3  Jan 2017  Bundle Y    5            R  Bundle Y        12
    6      3  Jan 2017  Bundle Y    5            S  Bundle Y         3
    

    剩下的只是重排和算术。

    不要小看手动的做事方式——它们有时是最简单的,你可以遵循的干净代码比你不能遵循的“聪明”代码要好得多。

    【讨论】:

      猜你喜欢
      • 2013-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-05
      相关资源
      最近更新 更多