取消透视多个变量 Pandas Dataframe

Posted

技术标签:

【中文标题】取消透视多个变量 Pandas Dataframe【英文标题】:Unpivot multiple variables Pandas Dataframe 【发布时间】:2021-03-29 05:45:49 【问题描述】:

问题

我有一个广泛的数据框,其中显示了不同时间段内各州的销售价格和数量。但是我想将数据帧转换(反透视)成一个长数据帧。在 SQL 中使用 UNPIVOT 很容易做到这一点,但我正在努力弄清楚如何在 pandas 中做到这一点。任何帮助将不胜感激!

我的尝试

我尝试过同时使用 pd.melt 和 pd.wide_to_long,但没有成功。下面的例子。

示例

df = pd.DataFrame('time': ['t1', 't2', 't3', 't4', 't5'],
                   'prod': ['A', 'B', 'C', 'D', 'E'],
                   'price_qld': [4, 3, 6, 3, 8],
                   'price_nsw': [7, 4, 7, 3, 5],
                   'price_vic': [9, 4, 6, 23, 7],
                   'vol_qld': [11, 43, 232, 234, 42],
                   'vol_nsw': [73, 44, 657, 53, 785],
                   'vol_vic': [95, 34, 666, 273, 87],
                   'flag_qld': [1, 1, 1, 1, 0],
                   'flag_nsw': [0, 1, 0, 1, 0],
                   'flag_vic': [1, 1, 1, 0, 1]
                   )
print(df)

new_df = pd.wide_to_long(df, ['price', 'vol', 'flag'], i=['time', 'prod'], j='State', sep='_')

当前数据框

  time prod  price_qld  price_nsw  ...  vol_vic  flag_qld  flag_nsw  flag_vic
0   t1    A          4          7  ...       95         1         0         1
1   t2    B          3          4  ...       34         1         1         1
2   t3    C          6          7  ...      666         1         0         1
3   t4    D          3          3  ...      273         1         1         0
4   t5    E          8          5  ...       87         0         0         1

所需的数据框

  time prod state  price  vol  flag
0   t1    A   qld      4   11     1
1   t1    A   nsw      7   73     0
2   t1    A   vic      9   95     1
3   t2    B   qld      3   43     1
4   t2    B   nsw      4   44     1
5   t2    B   vic      4   34     1
6   t3    C   qld      6  232     1
7   t3    C   nsw      7  657     0
8   t3    C   vic      6  666     1

【问题讨论】:

【参考方案1】:

你很接近,需要suffix='\w+' 来获取非整数作为后缀:

new_df = (pd.wide_to_long(df, ['price', 'vol', 'flag'],
                         i=['time', 'prod'],
                         j='State', 
                         sep='_', 
                         suffix='\w+')
             .reset_index())
    
print (new_df)
   time prod State  price  vol  flag
0    t1    A   qld      4   11     1
1    t1    A   nsw      7   73     0
2    t1    A   vic      9   95     1
3    t2    B   qld      3   43     1
4    t2    B   nsw      4   44     1
5    t2    B   vic      4   34     1
6    t3    C   qld      6  232     1
7    t3    C   nsw      7  657     0
8    t3    C   vic      6  666     1
9    t4    D   qld      3  234     1
10   t4    D   nsw      3   53     1
11   t4    D   vic     23  273     0
12   t5    E   qld      8   42     0
13   t5    E   nsw      5  785     0
14   t5    E   vic      7   87     1

另一种方法:

#convert all columns without separatot to MultiIndex
new_df = df.set_index(['time', 'prod'])
#split columns by separator
new_df.columns = new_df.columns.str.split('_', expand=True)
#reshape by stack
new_df = new_df.stack().reset_index().rename(columns='level_2':'state')
    
print (new_df)
   time prod state  flag  price  vol
0    t1    A   nsw     0      7   73
1    t1    A   qld     1      4   11
2    t1    A   vic     1      9   95
3    t2    B   nsw     1      4   44
4    t2    B   qld     1      3   43
5    t2    B   vic     1      4   34
6    t3    C   nsw     0      7  657
7    t3    C   qld     1      6  232
8    t3    C   vic     1      6  666
9    t4    D   nsw     1      3   53
10   t4    D   qld     1      3  234
11   t4    D   vic     0     23  273
12   t5    E   nsw     0      5  785
13   t5    E   qld     0      8   42
14   t5    E   vic     1      7   87

【讨论】:

Jezrael 再次救援!!,非常感谢老板,这很有效。我会在 10 分钟内接受答案。【参考方案2】:

另一种方法是使用来自pyjanitor 的pivot_longer 函数;它是 pandas 的 melt 的包装器,具有更大的灵活性:

In [219]: df.pivot_longer(index = ['time', 'prod'], 
                          names_to=('.value', 'state'), 
                          names_sep="_")
Out[219]: 
   time prod state  price  vol  flag
0    t1    A   qld      4   11     1
1    t2    B   qld      3   43     1
2    t3    C   qld      6  232     1
3    t4    D   qld      3  234     1
4    t5    E   qld      8   42     0
5    t1    A   nsw      7   73     0
6    t2    B   nsw      4   44     1
7    t3    C   nsw      7  657     0
8    t4    D   nsw      3   53     1
9    t5    E   nsw      5  785     0
10   t1    A   vic      9   95     1
11   t2    B   vic      4   34     1
12   t3    C   vic      6  666     1
13   t4    D   vic     23  273     0
14   t5    E   vic      7   87     1

In [220]: df.pivot_longer(index = ['time', 'prod'], 
                          names_to=('.value', 'state'), 
                          names_sep="_", 
                          sort_by_appearance=True)
Out[220]: 
   time prod state  price  vol  flag
0    t1    A   qld      4   11     1
1    t1    A   nsw      7   73     0
2    t1    A   vic      9   95     1
3    t2    B   qld      3   43     1
4    t2    B   nsw      4   44     1
5    t2    B   vic      4   34     1
6    t3    C   qld      6  232     1
7    t3    C   nsw      7  657     0
8    t3    C   vic      6  666     1
9    t4    D   qld      3  234     1
10   t4    D   nsw      3   53     1
11   t4    D   vic     23  273     0
12   t5    E   qld      8   42     0
13   t5    E   nsw      5  785     0
14   t5    E   vic      7   87     1

.value 在列被names_sep(_) 拆分后匹配(价格、卷、标志),而状态捕获names_sep 之后的值

【讨论】:

以上是关于取消透视多个变量 Pandas Dataframe的主要内容,如果未能解决你的问题,请参考以下文章

Pandas 组合了多个数据透视表

Pandas:取消组合并融化空格缩进的记录

Python Pandas Dataframe 数据透视表列和值顺序

Pandas:透视表(pivotTab)和交叉表(crossTab)

如何根据条件行值对 pandas 数据框进行取消堆叠或取消透视?

如何在 pandas 中为 2 级参数取消透视