Восстановите категориальную переменную из манекенов в pandas

pd.get_dummies позволяет преобразовать категориальную переменную в фиктивные переменные. Помимо того факта, что тривиально восстанавливать категориальную переменную, есть ли предпочтительный/быстрый способ сделать это?

Ответ 1

In [46]: s = Series(list('aaabbbccddefgh')).astype('category')

In [47]: s
Out[47]: 
0     a
1     a
2     a
3     b
4     b
5     b
6     c
7     c
8     d
9     d
10    e
11    f
12    g
13    h
dtype: category
Categories (8, object): [a < b < c < d < e < f < g < h]

In [48]: df = pd.get_dummies(s)

In [49]: df
Out[49]: 
    a  b  c  d  e  f  g  h
0   1  0  0  0  0  0  0  0
1   1  0  0  0  0  0  0  0
2   1  0  0  0  0  0  0  0
3   0  1  0  0  0  0  0  0
4   0  1  0  0  0  0  0  0
5   0  1  0  0  0  0  0  0
6   0  0  1  0  0  0  0  0
7   0  0  1  0  0  0  0  0
8   0  0  0  1  0  0  0  0
9   0  0  0  1  0  0  0  0
10  0  0  0  0  1  0  0  0
11  0  0  0  0  0  1  0  0
12  0  0  0  0  0  0  1  0
13  0  0  0  0  0  0  0  1

In [50]: x = df.stack()

# I don't think you actually need to specify ALL of the categories here, as by definition
# they are in the dummy matrix to start (and hence the column index)
In [51]: Series(pd.Categorical(x[x!=0].index.get_level_values(1)))
Out[51]: 
0     a
1     a
2     a
3     b
4     b
5     b
6     c
7     c
8     d
9     d
10    e
11    f
12    g
13    h
Name: level_1, dtype: category
Categories (8, object): [a < b < c < d < e < f < g < h]

Итак, мне кажется, что нам нужна функция, чтобы "делать" это, как будто это естественные операции. Может быть get_categories(), см. здесь

Ответ 2

Это было несколько лет, поэтому это, возможно, не было в наборе pandas когда этот вопрос изначально был задан, но этот подход мне немного легче. idxmax вернет индекс, соответствующий наибольшему элементу (т.е. тот, у кого есть 1). Мы делаем axis=1 потому что нам нужно имя столбца, где встречается 1.

EDIT: я не стал утруждать себя категорией, а не просто строкой, но вы можете сделать это так же, как @Jeff, обернув ее pd.Categoricalpd.Series, если это необходимо).

In [1]: import pandas as pd

In [2]: s = pd.Series(['a', 'b', 'a', 'c'])

In [3]: s
Out[3]: 
0    a
1    b
2    a
3    c
dtype: object

In [4]: dummies = pd.get_dummies(s)

In [5]: dummies
Out[5]: 
   a  b  c
0  1  0  0
1  0  1  0
2  1  0  0
3  0  0  1

In [6]: s2 = dummies.idxmax(axis=1)

In [7]: s2
Out[7]: 
0    a
1    b
2    a
3    c
dtype: object

In [8]: (s2 == s).all()
Out[8]: True

EDIT в ответ на комментарий @piRSquared: это решение действительно предполагает наличие 1 строки в строке. Я думаю, что это обычно формат. pd.get_dummies могут возвращать строки, все 0, если у вас есть drop_first=True или если есть значения NaN и dummy_na=False (по умолчанию) (в любом случае я не хватает?). Строка всех нулей будет обрабатываться так, как если бы это был экземпляр переменной, названной в первом столбце (например, a в примере выше).

Если drop_first=True, у вас нет способа узнать только из данных фиктивных ящиков только имя "первой" переменной, так что операция не является обратимой, если вы не храните дополнительную информацию; Я бы рекомендовал оставить drop_first=False (по умолчанию).

Поскольку dummy_na=False является значением по умолчанию, это может вызвать проблемы. Установите dummy_na=True когда вы вызываете pd.get_dummies если хотите использовать это решение для инвертирования "думмификации", и ваши данные содержат любые NaNs. Установка dummy_na=True всегда будет добавлять столбец "nan", даже если этот столбец равен 0, поэтому вы, вероятно, не хотите устанавливать это, если у вас фактически нет NaN. dummies = pd.get_dummies(series, dummy_na=series.isnull().any()) подходом может быть установка dummies = pd.get_dummies(series, dummy_na=series.isnull().any()). Что также приятно, так это то, что решение idxmax будет правильно восстанавливать ваш NaN (а не только строку, которая говорит "nan").

Также стоит упомянуть, что установка drop_first=True и dummy_na=False означает, что NaN становится неотличимым от экземпляра первой переменной, поэтому это должно быть сильно обескуражено, если ваш набор данных может содержать любые значения NaN.

Ответ 3

Это довольно поздний ответ, но, поскольку вы просите быстрый способ сделать это, я предполагаю, что вы ищете самую эффективную стратегию. На большом фрейме данных (например, 10000 строк) вы можете получить очень значительное ускорение скорости с помощью np.where вместо idxmax или get_level_values и получить тот же результат. Идея состоит в том, чтобы индексировать имена столбцов, где фиктивный фреймворк не равен 0:

Метод:

Используя те же данные образца, что и @Nathan:

>>> dummies
   a  b  c
0  1  0  0
1  0  1  0
2  1  0  0
3  0  0  1

s2 = pd.Series(dummies.columns[np.where(dummies!=0)[1]])

>>> s2
0    a
1    b
2    a
3    c
dtype: object

Ориентир:

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

s = pd.Series(np.random.choice(['a','b','c'], 10000))

dummies = pd.get_dummies(s)

def np_method(dummies=dummies):
    return pd.Series(dummies.columns[np.where(dummies!=0)[1]])

def idx_max_method(dummies=dummies):
    return dummies.idxmax(axis=1)

def get_level_values_method(dummies=dummies):
    x = dummies.stack()
    return pd.Series(pd.Categorical(x[x!=0].index.get_level_values(1)))

def dot_method(dummies=dummies):
    return dummies.dot(dummies.columns)

import timeit

# Time each method, 1000 iterations each:

>>> timeit.timeit(np_method, number=1000)
1.0491090340074152

>>> timeit.timeit(idx_max_method, number=1000)
12.119140846014488

>>> timeit.timeit(get_level_values_method, number=1000)
4.109266621991992

>>> timeit.timeit(dot_method, number=1000)
1.6741622970002936

Метод np.where примерно в 4 раза быстрее, чем метод get_level_values 11,5 раз быстрее, чем метод idxmax ! Он также бьет (но лишь немного) метод .dot() описанный в этом ответе на аналогичный вопрос

Все они возвращают тот же результат:

>>> (get_level_values_method() == np_method()).all()
True
>>> (idx_max_method() == np_method()).all()
True

Ответ 4

Настроить

Использование настройки @Jeff

s = Series(list('aaabbbccddefgh')).astype('category')
df = pd.get_dummies(s)

Если столбцы являются строками

и есть только 1 на строку

df.dot(df.columns)

0     a
1     a
2     a
3     b
4     b
5     b
6     c
7     c
8     d
9     d
10    e
11    f
12    g
13    h
dtype: object

numpy.where

Еще раз! Предполагая, что только 1 на строку

i, j = np.where(df)
pd.Series(df.columns[j], i)

0     a
1     a
2     a
3     b
4     b
5     b
6     c
7     c
8     d
9     d
10    e
11    f
12    g
13    h
dtype: category
Categories (8, object): [a, b, c, d, e, f, g, h]

numpy.where

Не допускать 1 на строку

i, j = np.where(df)
pd.Series(dict(zip(zip(i, j), df.columns[j])))

0   0    a
1   0    a
2   0    a
3   1    b
4   1    b
5   1    b
6   2    c
7   2    c
8   3    d
9   3    d
10  4    e
11  5    f
12  6    g
13  7    h
dtype: object

numpy.where

Где мы не предполагаем один 1 за строку, и мы отбрасываем индекс

i, j = np.where(df)
pd.Series(dict(zip(zip(i, j), df.columns[j]))).reset_index(-1, drop=True)

0     a
1     a
2     a
3     b
4     b
5     b
6     c
7     c
8     d
9     d
10    e
11    f
12    g
13    h
dtype: object

Ответ 5

Преобразование dat ["классификация"] в один горячий код и обратно!

import pandas as pd

from sklearn.preprocessing import LabelEncoder

dat["labels"]= le.fit_transform(dat["classification"])

Y= pd.get_dummies(dat["labels"])

tru=[]

for i in range(0, len(Y)):
  tru.append(np.argmax(Y.iloc[i]))

tru= le.inverse_transform(tru)

##Identical check!
(tru==dat["classification"]).value_counts()