pd.get_dummies
позволяет преобразовать категориальную переменную в фиктивные переменные. Помимо того факта, что тривиально восстанавливать категориальную переменную, есть ли предпочтительный/быстрый способ сделать это?
Восстановите категориальную переменную из манекенов в pandas
Ответ 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.Categorical
(и pd.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()