看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
pandas 的 pd.wide_to_long()
可完成长表转宽表的操作。与数据融合方法 pandas.melt()
相似,pd.wide_to_long()
可以通过列名的规则解析成行数据,比 pandas.melt()
更加灵活好用。pandas 提供了长转宽和宽转长表各种方法,宽型转长型数据有 melt
(数据融合) 和 wide_to_long
两种方法,长型数据转为宽型数据可以通过透视的功能实现,有 pivot
及 pivot_table
等。
pd.wide_to_long()
的语法为:
def wide_to_long(
df: DataFrame,
stubnames,
i,
j,
sep: str = "",
suffix: str = r"\d+"
) -> DataFrame
逻辑说明:
使用 stubnames ['A', 'B'],这个函数期望找到一个或多个带格式的列组 A-suffix1, A-suffix2,..., B-suffix1, B-suffix2,... 在生成的长格式中指定要调用此后缀的内容使用 j
(例如j='year'
)假设这些宽变量的每一行都由 i
(可以是单个列名或列名列表)数据框中所有剩余的变量都保持不变。
参数:
sep='-'
去除连字符suffix='(!?one|two)'
。 当所有后缀都是数字,它们被转换为 int64/float64返回:
注意:
所有额外的变量都保持不变,这只是使用 pandas.melt
藏在表面之下的机制,但被硬编码为“做正确的事”在一个典型的情况下。
np.random.seed(123)
df = pd.DataFrame({"A1970" : {0 : "a", 1 : "b", 2 : "c"},
"A1980" : {0 : "d", 1 : "e", 2 : "f"},
"B1970" : {0 : 2.5, 1 : 1.2, 2 : .7},
"B1980" : {0 : 3.2, 1 : 1.3, 2 : .1},
"X" : dict(zip(range(3), np.random.randn(3)))
})
df["id"] = df.index
df
'''
A1970 A1980 B1970 B1980 X id
0 a d 2.5 3.2 -1.085631 0
1 b e 1.2 1.3 0.997345 1
2 c f 0.7 0.1 0.282978 2
'''
pd.wide_to_long(df, ["A", "B"], i="id", j="year")
'''
X A B
id year
0 1970 -1.085631 a 2.5
1 1970 0.997345 b 1.2
2 1970 0.282978 c 0.7
0 1980 -1.085631 d 3.2
1 1980 0.997345 e 1.3
2 1980 0.282978 f 0.1
'''
具有多个 id 列:
df = pd.DataFrame({
'famid': [1, 1, 1, 2, 2, 2, 3, 3, 3],
'birth': [1, 2, 3, 1, 2, 3, 1, 2, 3],
'ht1': [2.8, 2.9, 2.2, 2, 1.8, 1.9, 2.2, 2.3, 2.1],
'ht2': [3.4, 3.8, 2.9, 3.2, 2.8, 2.4, 3.3, 3.4, 2.9]
})
df
'''
famid birth ht1 ht2
0 1 1 2.8 3.4
1 1 2 2.9 3.8
2 1 3 2.2 2.9
3 2 1 2.0 3.2
4 2 2 1.8 2.8
5 2 3 1.9 2.4
6 3 1 2.2 3.3
7 3 2 2.3 3.4
8 3 3 2.1 2.9
'''
l = pd.wide_to_long(df, stubnames='ht', i=['famid', 'birth'], j='age')
l
'''
ht
famid birth age
1 1 1 2.8
2 3.4
2 1 2.9
2 3.8
3 1 2.2
2 2.9
2 1 1 2.0
2 3.2
2 1 1.8
2 2.8
3 1 1.9
2 2.4
3 1 1 2.2
2 3.3
2 1 2.3
2 3.4
3 1 2.1
2 2.9
'''
从长到宽只需要创造性地使用“unstack”:
w = l.unstack()
w.columns = w.columns.map('{0[0]}{0[1]}'.format)
w.reset_index()
'''
famid birth ht1 ht2
0 1 1 2.8 3.4
1 1 2 2.9 3.8
2 1 3 2.2 2.9
3 2 1 2.0 3.2
4 2 2 1.8 2.8
5 2 3 1.9 2.4
6 3 1 2.2 3.3
7 3 2 2.3 3.4
8 3 3 2.1 2.9
'''
也会处理使用较少的列名:
np.random.seed(0)
df = pd.DataFrame({'A(weekly)-2010': np.random.rand(3),
'A(weekly)-2011': np.random.rand(3),
'B(weekly)-2010': np.random.rand(3),
'B(weekly)-2011': np.random.rand(3),
'X' : np.random.randint(3, size=3)})
df['id'] = df.index
df
'''
A(weekly)-2010 A(weekly)-2011 B(weekly)-2010 B(weekly)-2011 X id
0 0.548814 0.544883 0.437587 0.383442 0 0
1 0.715189 0.423655 0.891773 0.791725 1 1
2 0.602763 0.645894 0.963663 0.528895 1 2
'''
pd.wide_to_long(df, ['A(weekly)', 'B(weekly)'], i='id',
j='year', sep='-')
'''
X A(weekly) B(weekly)
id year
0 2010 0 0.548814 0.437587
1 2010 1 0.715189 0.891773
2 2010 1 0.602763 0.963663
0 2011 0 0.544883 0.383442
1 2011 1 0.423655 0.791725
2 2011 1 0.645894 0.528895
'''
如果我们有很多列,我们也可以使用正则表达式来查找stubnames并将该列表传递给 wide_to_long:
stubnames = sorted(
set([match[0] for match in df.columns.str.findall(
r'[A-B]\(.*\)').values if match != []])
)
list(stubnames)
# ['A(weekly)', 'B(weekly)']
以上所有示例都以整数作为后缀,可以将非整数作为后缀:
df = pd.DataFrame({
'famid': [1, 1, 1, 2, 2, 2, 3, 3, 3],
'birth': [1, 2, 3, 1, 2, 3, 1, 2, 3],
'ht_one': [2.8, 2.9, 2.2, 2, 1.8, 1.9, 2.2, 2.3, 2.1],
'ht_two': [3.4, 3.8, 2.9, 3.2, 2.8, 2.4, 3.3, 3.4, 2.9]
})
df
'''
famid birth ht_one ht_two
0 1 1 2.8 3.4
1 1 2 2.9 3.8
2 1 3 2.2 2.9
3 2 1 2.0 3.2
4 2 2 1.8 2.8
5 2 3 1.9 2.4
6 3 1 2.2 3.3
7 3 2 2.3 3.4
8 3 3 2.1 2.9
'''
l = pd.wide_to_long(df, stubnames='ht', i=['famid', 'birth'], j='age',
sep='_', suffix=r'\w+')
l
'''
ht
famid birth age
1 1 one 2.8
two 3.4
2 one 2.9
two 3.8
3 one 2.2
two 2.9
2 1 one 2.0
two 3.2
2 one 1.8
two 2.8
3 one 1.9
two 2.4
3 1 one 2.2
two 3.3
2 one 2.3
two 3.4
3 one 2.1
two 2.9
"""
更新时间:2021-10-27 23:42:57 标签:pandas 长宽表 宽表