融化后的分类列 pandas

Categorical column after melt in pandas

pandas 中的 melt 操作后是否有可能以分类变量列结束?

如果我这样设置数据:

import pandas as pd
import numpy as np

df = pd.DataFrame(
    np.random.randn(3, 5), 
    columns=["A", "B", "C", "D", "E"]
)
df["id"] = range(1, 4)
df
|    |         A |         B |         C |         D |          E |   id |
|----|-----------|-----------|-----------|-----------|------------|------|
|  0 | -0.406174 | -0.686917 | -0.172913 | -0.273074 | -0.0246714 |    1 |
|  1 |  0.323783 | -1.7731   |  1.57581  | -1.15671  | -1.23926   |    2 |
|  2 | -1.1426   | -0.591279 |  1.15265  |  0.326712 | -0.86374   |    3 |

然后申请

melted_df = df.melt(id_vars="id", value_vars=["A", "B", "C", "D", "E"])
melted_df
|    |   id | variable   |      value |
|----|------|------------|------------|
|  0 |    1 | A          | -0.406174  |
|  1 |    2 | A          |  0.323783  |
|  2 |    3 | A          | -1.1426    |
|  3 |    1 | B          | -0.686917  |
|  4 |    2 | B          | -1.7731    |
|  5 |    3 | B          | -0.591279  |
|  6 |    1 | C          | -0.172913  |
|  7 |    2 | C          |  1.57581   |
|  8 |    3 | C          |  1.15265   |
|  9 |    1 | D          | -0.273074  |
| 10 |    2 | D          | -1.15671   |
| 11 |    3 | D          |  0.326712  |
| 12 |    1 | E          | -0.0246714 |
| 13 |    2 | E          | -1.23926   |
| 14 |    3 | E          | -0.86374   |

variable 列的 dtype 是 object

melted_df.dtypes
id            int64
variable     object
value       float64
dtype: object

我希望这是 category。我知道,我可以通过以下方式轻松转换它:

melted_df["variable"].astype("category")

但是对于大型数据集,我想避免这种开销。在 documentation 中我没有找到这样的选项,但由于结果列根据定义包含分类数据,我认为一定存在这种可能性。

我认为 melt 不可能,因为当它创建该列时,它会推断 dtype 而 'category' 不是 dtype 当前 pandas推断。 (这是一个相关问题,它无法正确推断 Int32 dtypes )。

如果您首先转换列,

stack 将保留分类数据类型。 stack 的顺序与 melt 略有不同,但数据是相同的。 stack 在命名结果列时也有点笨拙。

df = df.set_index('id')
df.columns = df.columns.astype('category')

res = (df.stack()
         .rename_axis(['id', 'variable'])
         .rename('value')
         .reset_index())
#    id variable     value
#0    1        A  0.424781
#1    1        B -0.317107
#2    1        C  0.731121
#3    1        D  0.042642
#4    1        E  0.648352
#...
#13   3        D -0.889600
#14   3        E -1.822898

res.dtypes
#id             int64
#variable    category
#value        float64
#dtype: object

一个有效的选择是
pivot_longer from pyjanitor,使用names_transform参数:

# pip install pyjanitor
import pandas as pd
import janitor

np.random.seed(456)

df = pd.DataFrame(
    np.random.randn(3, 5), 
    columns=["A", "B", "C", "D", "E"]
)
df["id"] = range(1, 4)
df

 A         B         C         D         E  id
0 -0.668129 -0.498210  0.618576  0.568692  1.350509   1
1  1.629589  0.301966  0.449483 -0.345811 -0.315231   2
2 -2.015971 -1.130231 -1.111846  0.237851 -0.325130   3

result = df.pivot_longer(index = 'id', names_transform = 'category')
result
    id variable     value
0    1        A -0.668129
1    2        A  1.629589
2    3        A -2.015971
3    1        B -0.498210
4    2        B  0.301966
5    3        B -1.130231
6    1        C  0.618576
7    2        C  0.449483
8    3        C -1.111846
9    1        D  0.568692
10   2        D -0.345811
11   3        D  0.237851
12   1        E  1.350509
13   2        E -0.315231
14   3        E -0.325130

result.dtypes

id             int64
variable    category
value        float64
dtype: object