Pandas эквивалент Stata encode

Я ищу способ репликации поведения encode в Stata, который преобразует категориальный столбец строки в числовой столбец.

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 из строк в целые числа, сопоставляя каждую уникальную строку с (произвольным) целым числом от 1 до 1. Это приведет к:

     val
cat     
1     10
1     20
2     30

Или, как хорошо:

  cat  val
0   1   10
1   1   20
2   2   30

Любые предложения?

Большое спасибо, как всегда, Rob

Ответы

Ответ 1

Команда Stata encode начинается с строковой переменной и создает новую целочисленную переменную с метками, сопоставленными с исходной строковой переменной. Прямым аналогом этого в pandas стал бы категориальный тип переменной, который стал полноценной частью pandas начиная с 0,15 (которая была выпущена после того, как этот вопрос был изначально задан и ответил).

Смотрите документацию здесь.

Чтобы продемонстрировать этот пример, команда 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

Ответ 2

Вы можете использовать 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

Вы можете добавить 1 к labels, если хотите скопировать поведение Stata:

x['cat'] = labels+1

Ответ 3

Предполагая, что у вас есть фиксированный набор заглавных английских букв в качестве вашей категориальной переменной, вы также можете сделать это:

x['cat'] = x.cat.map(lambda x: ord(x) - 64)

Я считаю, что это немного взломать. Но опять же, в Python, лучше всего было бы определить отображение от символов до целых чисел, которые вы хотите, например

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

а затем do

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

или что-то подобное.

Это выше, чем полагаться на соглашения встроенных функций для вашего целочисленного сопоставления, по многим причинам и (IMO), это такие вещи, которые "чувствуют" "неприятные преобразования" программисту-аналитику, но на самом деле представляют собой важные метаданные о написанном вами программном обеспечении, которые раскрывают реальную слабость глобальных функций удобства на языках более высокого уровня, таких как MATLAB, STATA и т.д. Даже если есть встроенная функция, которая случайно соприкасается с конкретным соглашением, которое вы хотите (произвольное соглашение о том, что "A" отображается в 1, "B" отображается на 2 и т.д.), это не делает его хорошей идеей для его использования.