看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gr99123(备注: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 长宽表 宽表