【问题标题】:Pandas long to wide reshape, by two variables熊猫从长到宽重塑,由两个变量
【发布时间】:2014-05-12 23:56:11
【问题描述】:

我有长格式的数据,正在尝试重新调整为宽格式,但似乎没有一种直接的方法可以使用 melt/stack/unstack:

Salesman  Height   product      price
  Knut      6        bat          5
  Knut      6        ball         1
  Knut      6        wand         3
  Steve     5        pen          2

变成:

Salesman  Height    product_1  price_1  product_2 price_2 product_3 price_3  
  Knut      6        bat          5       ball      1        wand      3
  Steve     5        pen          2        NA       NA        NA       NA

我认为 Stata 可以用 reshape 命令做这样的事情。

【问题讨论】:

  • 你真的关心两个旋转变量是否交错:product_1 price_1 product_2 price_2 product_3 price_3?他们可以只是product_1 product_2 ... price_1 price_2 ...吗?
  • 是的,没关系。

标签: python pandas stata reshape


【解决方案1】:

一个简单的支点可能足以满足您的需求,但这是我为重现您想要的输出所做的:

df['idx'] = df.groupby('Salesman').cumcount()

只需在组内添加计数器/索引即可让您大部分时间到达那里,但列标签不会如您所愿:

print df.pivot(index='Salesman',columns='idx')[['product','price']]

        product              price        
idx            0     1     2      0   1   2
Salesman                                   
Knut         bat  ball  wand      5   1   3
Steve        pen   NaN   NaN      2 NaN NaN

为了更接近您想要的输出,我添加了以下内容:

df['prod_idx'] = 'product_' + df.idx.astype(str)
df['prc_idx'] = 'price_' + df.idx.astype(str)

product = df.pivot(index='Salesman',columns='prod_idx',values='product')
prc = df.pivot(index='Salesman',columns='prc_idx',values='price')

reshape = pd.concat([product,prc],axis=1)
reshape['Height'] = df.set_index('Salesman')['Height'].drop_duplicates()
print reshape

         product_0 product_1 product_2  price_0  price_1  price_2  Height
Salesman                                                                 
Knut           bat      ball      wand        5        1        3       6
Steve          pen       NaN       NaN        2      NaN      NaN       5

编辑:如果您想将该过程推广到更多变量,我认为您可以执行以下操作(尽管它可能不够高效):

df['idx'] = df.groupby('Salesman').cumcount()

tmp = []
for var in ['product','price']:
    df['tmp_idx'] = var + '_' + df.idx.astype(str)
    tmp.append(df.pivot(index='Salesman',columns='tmp_idx',values=var))

reshape = pd.concat(tmp,axis=1)

@Luke 说:

我认为 Stata 可以用 reshape 命令做这样的事情。

你可以,但我认为你还需要一个组内计数器来在 stata 中重塑以获得你想要的输出:

     +-------------------------------------------+
     | salesman   idx   height   product   price |
     |-------------------------------------------|
  1. |     Knut     0        6       bat       5 |
  2. |     Knut     1        6      ball       1 |
  3. |     Knut     2        6      wand       3 |
  4. |    Steve     0        5       pen       2 |
     +-------------------------------------------+

如果您添加idx,那么您可以在stata 中进行重塑:

reshape wide product price, i(salesman) j(idx)

【讨论】:

  • 效果很好。对于熊猫来说,这将是一个不错的功能。已经有wide_to_long了,为什么不是另一个方向。
  • 同意...这种重塑是 stata 中更有用的工具之一。
  • 是的,这基本上就是我最终要做的,尽管您还必须分离出不会改变的列,例如高度,删除重复项,然后再将它们连接起来。
【解决方案2】:

这是另一个更充实的解决方案,取自Chris Albon's site

创建“长”数据框

raw_data = {'patient': [1, 1, 1, 2, 2],
                'obs': [1, 2, 3, 1, 2],
          'treatment': [0, 1, 0, 1, 0],
              'score': [6252, 24243, 2345, 2342, 23525]}

df = pd.DataFrame(raw_data, columns = ['patient', 'obs', 'treatment', 'score'])

制作“宽”数据

df.pivot(index='patient', columns='obs', values='score')

【讨论】:

    【解决方案3】:

    有点旧,但我会为其他人发布这个。

    你想要的可以实现,但你可能不应该想要它;) Pandas 支持行和列的分层索引。 在 Python 2.7.x ...

    from StringIO import StringIO
    
    raw = '''Salesman  Height   product      price
      Knut      6        bat          5
      Knut      6        ball         1
      Knut      6        wand         3
      Steve     5        pen          2'''
    dff = pd.read_csv(StringIO(raw), sep='\s+')
    
    print dff.set_index(['Salesman', 'Height', 'product']).unstack('product')
    

    产生一个可能比您正在寻找的更方便的表示

                    price             
    product          ball bat pen wand
    Salesman Height                   
    Knut     6          1   5 NaN    3
    Steve    5        NaN NaN   2  NaN
    

    使用 set_index 和 unstacking 与单个函数作为枢轴相比的优势在于,您可以将操作分解为清晰的小步骤,从而简化调试。

    【讨论】:

    • 你为什么还在使用 Python 2.7?在 Python 3 中怎么样?
    • 对于 python3,你做from io import StringIO 并使用 print 作为一个函数,一切都很好。设置索引和取消堆叠的基本思想是相同的。
    【解决方案4】:

    Karl D 的解决方案是问题的核心。但我发现旋转所有内容要容易得多(使用.pivot_table,因为有两个索引列)然后sort 并分配列以折叠MultiIndex

    df['idx'] = df.groupby('Salesman').cumcount()+1
    df = df.pivot_table(index=['Salesman', 'Height'], columns='idx', 
                        values=['product', 'price'], aggfunc='first')
    
    df = df.sort_index(axis=1, level=1)
    df.columns = [f'{x}_{y}' for x,y in df.columns]
    df = df.reset_index()
    

    输出:

      Salesman  Height  price_1 product_1  price_2 product_2  price_3 product_3
    0     Knut       6      5.0       bat      1.0      ball      3.0      wand
    1    Steve       5      2.0       pen      NaN       NaN      NaN       NaN
    

    【讨论】:

    • 非常感谢。虽然我的数据框中已经有了 idx col,但您的解决方案能够将重复措施从长格式变为宽格式。 Pandas 为wide_to_long() 提供此功能,但不为 long_to_wide 提供此功能。伤心。
    • 与 Stata 背景相当相关。
    【解决方案5】:
    pivoted = df.pivot('salesman', 'product', 'price')
    

    页。 192 Python 用于数据分析

    【讨论】:

    • 使用此方法时(来自书中),即使我使用了 df.drop_duplicates(),我也会收到“ValueError:索引包含重复条目,无法重塑”
    【解决方案6】:

    一个老问题;这是对已经很好的答案的补充。来自pyjanitorpivot_wider 可能有助于作为从长到宽重塑的抽象(它是pd.pivot 的包装器):

    # pip install pyjanitor
    import pandas as pd
    import janitor as jn
    
    idx = df.groupby(['Salesman', 'Height']).cumcount().add(1)
    
    (df.assign(idx = idx)
       .pivot_wider(index = ['Salesman', 'Height'], names_from = 'idx')
    )
     
      Salesman  Height product_1 product_2 product_3  price_1  price_2  price_3
    0     Knut       6       bat      ball      wand      5.0      1.0      3.0
    1    Steve       5       pen       NaN       NaN      2.0      NaN      NaN
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-09
      • 1970-01-01
      • 2018-05-05
      • 1970-01-01
      相关资源
      最近更新 更多