如何将重复的列转置为行 Pandas

How to Transpose Repeating Columns to Rows Pandas

我有一个看起来像这样的 df1:

year    site    1   2   3   year    site    1   2   3   year    site    1   2   3   year    site    1   2   3
1991    A   4.1 5.9 4.1 1991    B   3.3 4.1 4.1 1991    C   4.1 0.6 4.1 1991    D   4.1 4.1 4.1
1992    A   6.2 5.7 6.2 1992    B   6.2 7.1 6.2 1992    C   6.2 6.2 6.2 1992    D   6.2 9.5 7.4
1993    A   2.6 1.9 4.7 1993    B   2.6 6.2 2.6 1993    C   5.4 8.3 2.6 1993    D   0.4 2.6 2.6

而且,我无法将列(月份 (1,2,3))转换为每个站点的行,以便我重塑的 df1 或 df2 看起来像这样:

year    month   Site A  Site B  Site C  Site D
1991    1       4.1     3.3     4.1     4.1
1991    2       5.9     4.1     0.6     4.1
1991    3       4.1     4.1     4.1     4.1
1992    1       6.2     6.2     6.2     6.2
1992    2       5.7     7.1     6.2     9.5
1992    3       6.2     6.2     6.2     7.4
1993    1       2.6     2.6     5.4     0.4
1993    2       1.9     6.2     8.3     2.6
1993    3       4.7     2.6     2.6     2.6

我试过使用 'melt' 和 'stack',但我不明白如何引用重复的月份 (1,2,3)。谢谢,

使用硬索引切片和重塑尝试以下操作:

#Create input dataframe
np.random.seed(0)
df = pd.concat([pd.DataFrame({'year':[1991, 1992, 1993],
                  'site':[i]*3,
                  1:np.round(np.random.randint(2,8,3)+np.random.random(3),1),
                  2:np.round(np.random.randint(2,8,3)+np.random.random(3),1),
                  3:np.round(np.random.randint(2,8,3)+np.random.random(3),1)}) for i in [*'ABC']], axis=1)

# index slice columns of the dataframe
df_out = pd.concat([df.iloc[:,i:i+5] for i in range(0,df.shape[1],5)])

# Reshape with melt, set_index, and unstack
df_out =  df_out.melt(['year', 'site'], var_name='month')\
      .set_index(['year', 'month', 'site'])['value']\
      .unstack('site').add_prefix('Site ')\
      .reset_index()

print(df_out)

输出:

site  year  month  Site A  Site B  Site C
0     1991      1     6.6     6.0     5.5
1     1991      2     7.3     5.5     7.6
2     1991      3     3.9     2.5     4.7
3     1992      1     7.5     2.1     5.6
4     1992      2     4.1     2.8     7.9
5     1992      3     2.1     3.8     2.1
6     1993      1     2.4     5.9     4.0
7     1993      2     6.3     3.1     2.7
8     1993      3     3.1     3.1     3.7

我们可以创建一个新的列级别,每个列 header 都按位置与 groupby cumcount 分组。这样做的好处是列不需要按固定顺序排列,只要它们的名称相同即可。

然后使用 stack to get all the separate groups into rows, set_index to exclude the site and year columns, then stack and unstack 按网站而不是月份分组:

# calculate new MultiIndex level
midx = pd.MultiIndex.from_arrays([
    df.columns,
    df.columns.to_series().groupby(level=0).cumcount()
])
new_df = (
    df.set_axis(midx, axis=1)  # replace columns
        .stack()  # Move all groups into rows
        .set_index(['site', 'year'])  # save site and year
        .rename_axis(columns='Month')  # rename column axis to Month
        .stack()  # Move all month columns to rows
        .unstack(level='site')  # Convert to site rows to columns
        .add_prefix('Site ')  # Add Prefix
        .rename_axis(columns=None)  # Remove Axis Name
        .reset_index()  # Restore Range Index
)

new_df:

   year  Month Site A Site B Site C Site D
0  1991      1     A1     B1     C1     D1
1  1991      2     A2     B2     C2     D2
2  1991      3     A3     B3     C3     D3
3  1992      1     A1     B1     C1     D1
4  1992      2     A2     B2     C2     D2
5  1992      3     A3     B3     C3     D3
6  1993      1     A1     B1     C1     D1
7  1993      2     A2     B2     C2     D2
8  1993      3     A3     B3     C3     D3

比较冒险的方法是reshape DataFrame.values 基于一定数量的唯一列(5)然后剩下的和上面一样:

unique_cols = df.columns.unique().tolist()
new_df = (
    pd.DataFrame(
        # reshape dataframe into len(unique_cols) columns
        # and however many rows
        df.values.reshape((-1, len(unique_cols))),
        columns=unique_cols  # restore column names
    ).set_index(['year', 'site'])
        .rename_axis(columns='Month')  # rename column axis to Month
        .stack()  # Move all month columns to rows
        .unstack(level='site')  # Convert to site rows to columns
        .add_prefix('Site ')  # Add Prefix
        .rename_axis(columns=None)  # Remove Axis Name
        .reset_index()  # Restore Range Index
)

new_df:

   year  Month Site A Site B Site C Site D
0  1991      1     A1     B1     C1     D1
1  1991      2     A2     B2     C2     D2
2  1991      3     A3     B3     C3     D3
3  1992      1     A1     B1     C1     D1
4  1992      2     A2     B2     C2     D2
5  1992      3     A3     B3     C3     D3
6  1993      1     A1     B1     C1     D1
7  1993      2     A2     B2     C2     D2
8  1993      3     A3     B3     C3     D3

*请注意,此方法仅在可以保证 DataFrame 结构时才有效,因为我们通过使用 numpy 重塑来绕过所有 pandas 数据完整性检查。


使用的设置:

from itertools import chain

import pandas as pd

sites = "ABCD"
df = pd.DataFrame(
    chain.from_iterable([range(1991, 1994),
                         [f'{v}'] * 3,
                         [f'{v}1'] * 3,
                         [f'{v}2'] * 3,
                         [f'{v}3'] * 3] for v in sites)
).T
df.columns = ['year', 'site', 1, 2, 3] * len(sites)

删节df:

   year site   1   2   3  year site   1  ...   1   2   3  year site   1   2   3
0  1991    A  A1  A2  A3  1991    B  B1  ...  C1  C2  C3  1991    D  D1  D2  D3
1  1992    A  A1  A2  A3  1992    B  B1  ...  C1  C2  C3  1992    D  D1  D2  D3
2  1993    A  A1  A2  A3  1993    B  B1  ...  C1  C2  C3  1993    D  D1  D2  D3

@HenryEcker 的解决方案是正确且首选的解决方案,尤其是当列的结构与上述不同时。

下面的解决方案使用 pivot_longer from pyjanitor,并假定列的顺序(如果您不确定顺序,@HenryEcker 的解决方案是安全的并且可以完成工作,具有 cumount 独特的想法):

# using Henry's data
# pip install pyjanitor
import janitor
import pandas as pd

df = df.rename(columns = str)
unique_columns = [*df.columns.unique()

(df.pivot_longer(names_to = unique_columns], 
                 names_pattern = unique_columns)
   .pivot('year', 'site')
   .stack(level = 0)
   .add_prefix('Site')
   .rename_axis(columns = None, 
                index = ['year', 'month'])
   .reset_index()
)
 
   year month SiteA SiteB SiteC SiteD
0  1991     1    A1    B1    C1    D1
1  1991     2    A2    B2    C2    D2
2  1991     3    A3    B3    C3    D3
3  1992     1    A1    B1    C1    D1
4  1992     2    A2    B2    C2    D2
5  1992     3    A3    B3    C3    D3
6  1993     1    A1    B1    C1    D1
7  1993     2    A2    B2    C2    D2
8  1993     3    A3    B3    C3    D3