如何将重复的列转置为行 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
我有一个看起来像这样的 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