【问题标题】:pandas get mapping of categories to integer valuepandas 将类别映射到整数值
【发布时间】:2019-06-20 22:58:20
【问题描述】:

我可以将分类列转换为它们的分类代码,但我如何准确地了解它们的映射?示例:

df_labels = pd.DataFrame({'col1':[1,2,3,4,5], 'col2':list('abcab')})
df_labels['col2'] = df_labels['col2'].astype('category')  

df_labels 看起来像这样:

   col1 col2
0     1    a
1     2    b
2     3    c
3     4    a
4     5    b

如何将猫代码准确映射到猫类别? 下面的 stackoverflow 响应说要枚举类别。但是,我不确定枚举是否是 cat.codes 生成整数值的方式。有没有更准确的方法?

Get mapping of categorical variables in pandas

>>> dict( enumerate(df.five.cat.categories) )

{0: 'bad', 1: 'good'}

获得上述格式但准确的映射的好方法是什么?

【问题讨论】:

  • 仅供参考,我已经更新了我的答案(您链接到的)并添加了一些解释/验证。我相信它是准确的,但如果您能详细说明您认为不准确的地方,我很乐意对其进行改进。

标签: python pandas


【解决方案1】:

我使用:

dict([(category, code) for code, category in enumerate(df_labels.col2.cat.categories)])

# {'a': 0, 'b': 1, 'c': 2}

【讨论】:

  • 请注意,这大致等同于 OP 拒绝的答案:dict(enumerate(df.five.cat.categories)),除了它从例如切换键和值。 0:'a''a':0 这是一个微小的区别,因为这里的键和值都是唯一的,所以键/值顺序在某种意义上是无关紧要的,而且它也很容易反转。 (我认为OP拒绝的答案(我的!)实际上是正确的,所以我也认为这个答案也是正确的!)
【解决方案2】:

编辑答案(删除cat.categories并将list更改为dict):

>>> dict(zip(df_labels.col2.cat.codes, df_labels.col2))

{0: 'a', 1: 'b', 2: 'c'}

部分cmets所指的原始答案:

>>> list(zip(df_labels.col2.cat.codes, df_labels.col2.cat.categories))

[(0, 'a'), (1, 'b'), (2, 'c')]

正如 cmets 所指出的,原始答案在此示例中有效,因为前三个值恰好是 [a,b,c],但如果它们是 [c,b,a][b,c,a],则会失败。

【讨论】:

  • 好的,谢谢!需要将set 放在前面,因为我只想要唯一的映射:set(list(zip(df_labels.col2.cat.codes, df_labels.col2.cat.categories)))
  • 我认为这个答案只适用于 col2 的排序方式。 len(cat.categories) 是 3 而len(cat.codes) 是 5。
  • 这是一个错误的答案,因为ser.cat.categories 将返回类别中的所有唯一值,而不是系列中项目的对应标签。
  • @JohnE 随时编辑。我无法删除我的答案,因为它已被接受
  • 谢谢,@boud,我编辑了它(同时保留原件并附上注释)。请根据需要添加其他修改。
【解决方案3】:

如果您想将每个列/数据系列从分类转换回原始数据,您只需反转您在数据框的 for 循环中所做的操作。有两种方法可以做到这一点:

  1. 要返回原始 Series 或 numpy 数组,请使用 Series.astype(original_dtype)np.asarray(categorical)

  2. 如果你已经有代码和类别,你可以使用from_codes()constructor在普通构造函数模式下保存分解步骤。

pandas: Categorical Data


from_codes的用法

official documentation 一样,它从代码和类别数组中生成分类类型。

splitter = np.random.choice([0,1], 5, p=[0.5,0.5])
s = pd.Series(pd.Categorical.from_codes(splitter, categories=["train", "test"]))
print splitter
print s

给予

[0 1 1 0 0]
0    train
1     test
2     test
3    train
4    train
dtype: category
Categories (2, object): [train, test]

为您的代码

# after your previous conversion
print df['col2']
# apply from_codes, the 2nd argument is the categories from mapping dict
s = pd.Series(pd.Categorical.from_codes(df['col2'], list('abcde')))
print s

给予

0    0
1    1
2    2
3    0
4    1
Name: col2, dtype: int8
0    a
1    b
2    c
3    a
4    b
dtype: category
Categories (5, object): [a, b, c, d, e]

【讨论】:

  • 没有太多关于使用from_codes()的文档。你能告诉我如何应用它吗?
  • 我明白了,我只想要唯一的映射值,而不是完整的映射。例如 { 0 : 'a', 1 : 'b', 2 : 'c' }
  • 然后您可以使用代码和类别轻松地自己构建地图。但是您不能通过 Python 字典来维护顺序,而是在 @Boud 答案中使用两个列表或一个元组列表。
【解决方案4】:

OP 要求相对于链接问题中的答案“准确”的内容:

dict(enumerate(df_labels.col2.cat.categories))

# {0: 'a', 1: 'b', 2: 'c'}

我相信上述答案确实是准确的(完全披露:这是我在另一个问题中的答案,我正在为之辩护)。另请注意,它大致相当于@pomber 的答案,除了键和值的顺序是相反的。 (由于键和值都是唯一的,因此排序在某种意义上是无关紧要的,因此很容易反转)。

但是,可以说以下方式更安全,或者至少对其工作方式更透明:

dict(zip(df_labels.col2.cat.codes, df_labels.col2))

# {0: 'a', 1: 'b', 2: 'c'}

这在精神上与@boud 的答案相似,但通过将df_labels.col2.cat.codes 替换为df_labels.col2 来纠正错误。它还将list() 替换为dict(),这似乎更适合映射并自动消除重复项。

注意zip() 的两个参数的长度是len(df),而df_labels.col2.cat.categories 的长度是唯一值的计数,通常比len(df) 短得多。

还请注意,此方法效率很低,因为它将0 映射到'a' 两次,'b' 也是如此。在大型数据帧中,速度差异可能非常大。但它不会导致任何错误,因为dict() 会删除这样的冗余——只是它的效率会比其他方法低得多。

【讨论】:

    猜你喜欢
    • 2017-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多