Pandas中Stata的encode等效功能

16

我正在寻找一种在Stata中复制encode行为的方法,该方法将把一个分类字符串列转换为数字列。

x = pd.DataFrame({'cat':['A','A','B'], 'val':[10,20,30]})
x = x.set_index('cat')

导致的结果是:

     val
cat     
A     10
A     20
B     30

我想将“cat”列从字符串转换为整数,将每个唯一的字符串映射到一个(任意)一对一的整数。结果如下:

     val
cat     
1     10
1     20
2     30

或者,同样好:

  cat  val
0   1   10
1   1   20
2   2   30

有什么建议吗?

一如既往地感谢, 罗布


也许可以这样做:DataFrame([(i[1], i[0]) for i in enumerate(set(x.index))]),然后再合并? - lowtech
重要细节:这不是Stata的encode所做的。它会产生一对一的映射。 - Nick Cox
@NickCox 我不明白这怎么不是一对一映射。每个实例的 'A' 变成 1,每个实例的 'B' 变成 2 等等。 - LondonRob
@NickCox,正在进行映射的是“cat”列,而不是“val”列。 “val”列保持不变,并且与示例无关。 重要的是,“cat”按照我的示例从“['A','A','B']”变为“[1,1,2]”。 - LondonRob
根据@NickCox的评论,我将我尝试做的事情的描述更加明确。 - LondonRob
显示剩余2条评论
3个回答

17
您可以使用 pd.factorize:
import pandas as pd

x = pd.DataFrame({'cat':('A','A','B'), 'val':(10,20,30)})
labels, levels = pd.factorize(x['cat'])
x['cat'] = labels
x = x.set_index('cat')
print(x)
产出。
     val
cat     
0     10
0     20
1     30

如果您希望复制Stata的行为,可以将1添加到labels

x['cat'] = labels+1

2
另一种获取 [0,0,1] 的方法是查看 pd.Categorical(seq).labels - DSM
谢谢,@DSM。看了一下源代码,我发现Categorical调用了factorize函数。 - unutbu
感谢@unutbu。FYI:这是一种制作美丽的分类散点图的绝妙方法,使用文本列作为类别。 - LondonRob
4
@unutbu这应该放在文档中,你能否为这里的某个位置做一个PR:http://pandas.pydata.org/pandas-docs/dev/reshaping.html#computing-indicator-dummy-variables - Jeff
请使用主要代码库;当0.13版本发布时,稳定文档将会更新。 - Jeff
显示剩余2条评论

9
Stata的encode命令从一个字符串变量开始,并创建一个新的整数变量,标签映射到原始字符串变量。在pandas中,直接类似于此的操作现在是分类变量类型,它成为从0.15版本开始的pandas的一部分(该版本发布后,最初提出和回答这个问题)。请参阅此处的文档。
为了演示此示例,Stata命令将如下所示:
encode cat, generate(cat2)

相反,使用pandas命令:

x['cat2'] = x['cat'].astype('category')

  cat  val cat2
0   A   10    A
1   A   20    A
2   B   30    B

与Stata的encode函数一样,数据被存储为整数,在默认输出中显示为字符串。
您可以使用分类访问器cat来查看底层整数。 (因此,您可能不希望使用“cat”作为列名。)
x['cat2'].cat.codes

0    0
1    0
2    1

1
我已经试了几个小时了!一直在搜索将对象转换为整数,或将分类转换为数字并疯狂地尝试。我使用的是pandas 16.2版本(anaconda当前版本)。 - James Owers
+1000 df['a'].cat.codes 真是救星!一直在网上搜寻替代 sklearn 的 DictVectorizer 或 LabelEncoder 的方法。这个方法与 OneHotEncoder 结合使用,可以很好地配合 sklearn-pandas 使用。 - cmcapellan

1
假设你的分类变量是一组固定的大写英文字母,你也可以这样做:
x['cat'] = x.cat.map(lambda x: ord(x) - 64)

我认为这有点像是一个hack。但在Python中,最好的方法是定义一个从字符到整数的映射,以便你可以选择自己需要的,比如:

my_map = {"A":1, ...} 
# e.g.: {x:ord(x)-64  for x in string.ascii_uppercase}
# if that's the convention you happen to desire.

然后执行

x['cat'] = x.cat.map(lambda x: my_map[x])

或类似的东西。

这比依赖内置函数的约定进行整数映射要优越得多,有许多原因,而且(在我看来)正是像这样的东西让程序员分析师感觉像是麻烦的转换,但实际上它们代表了你正在编写的软件的重要元数据,揭示了高级语言(如MATLAB、STATA等)中全局便利函数的真正弱点。即使有一个内置函数恰好遵循您想要使用的特定约定(将"A"映射到1,"B"映射到2等任意约定),使用它也不是一个好主意。


@Phillip Cloud 我想这取决于个人口味,是否希望 int 以那种方式运作。由于在 Python 中 int(x) 只是 x.__int__() 的语法糖,所以我不像你那样看待它。我不认为单字符的 str 变量应该有不同于多字符 str 变量的 __int__,这为想要使用 ord 这样的函数提供了区别,但这只是我的观点。 - ely
@EMS 你对Stata的经验并不足以正确拼写其名称或知道Stata命令和Stata函数之间的区别。如果经验时间是一个论点,那么请感受一下我22年与Stata共度的沉甸甸。更严肃、更重要的是,你对“encode”的评论仍然令人困惑,因为你已经改变了你的论点(实际上是一种断言),认为如果以你认为可疑的方式使用语言特性,则表明该特性有问题。这更多地反映了你个人的品味,而不是其他任何事情。 - Nick Cox
我只能回应你对我的批评,而不是我的论点。 - Nick Cox
@EMS 我认为你错了。关于 __int__ 的行为,我同意你的观点。我给 @NickCox 提供了一个例子。我猜我应该提一下这个。 - Phillip Cloud
哦,我明白了,我的错。我误读了你的评论,以为MATLAB的行为更可取。 - ely
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接