Преобразование категориальных данных в набор пустышек
У меня есть данные, как это:-
|--------|---------|
| Col1 | Col2 |
|--------|---------|
| X | a,b,c |
|--------|---------|
| Y | a,b |
|--------|---------|
| X | b,d |
|--------|---------|
Я хочу преобразовать эти категориальные данные в фиктивные переменные. Так как мои данные большие, это дает ошибку памяти, если я использую get_dummies()
из панд. Я хочу, чтобы мой результат был таким:-
|------|------|------|------|------|------|
|Col_X |Col_Y |Col2_a|Col2_b|Col2_c|Col2_d|
|------|------|------|------|------|------|
| 1 | 0 | 1 | 1 | 1 | 0 |
|------|------|------|------|------|------|
| 0 | 1 | 1 | 1 | 0 | 0 |
|------|------|------|------|------|------|
| 1 | 0 | 0 | 1 | 0 | 1 |
|------|------|------|------|------|------|
Я пытался конвертировать Col2, используя это, но получая MemoryError, так как данные велики, и в col2 также много вариаций.
Так,
1) Как я могу преобразовать несколько категориальных столбцов в фиктивную переменную?
2) pandas get_dummy () выдает ошибку памяти, так как я могу это исправить?
2 ответа
Я хотел бы также дать свое решение. И я хотел бы поблагодарить @James-dellinger за ответ. Так вот мой подход
df = pd.DataFrame({'Col1': ['X', 'Y', 'X'],
'Col2': ['a,b,c', 'a,b', 'b,d']})
df
Col1 Col2
0 X a,b,c
1 Y a,b
2 X b,d
Сначала я делю значения Col2 и преобразовываю их в значения столбцов.
df= pd.DataFrame(df['Col2'].str.split(',',3).tolist(),columns = ['Col1','Col2','Col3'])
df
Col1 Col2 Col3
0 a b c
1 a b None
2 b d None
Затем я применил фиктивное создание к этому фрейму данных без указания префикса.
df=pd.get_dummies(df, prefix="")
df
_a _b _b _d _c
0 1 0 1 0 1
1 1 0 1 0 0
2 0 1 0 1 0
Теперь, чтобы получить желаемый результат, мы можем суммировать все повторяющиеся столбцы.
df.groupby(level=0, axis=1).sum()
df
_a _b _c _d
0 1 1 1 0
1 1 1 0 0
2 0 1 0 1
Для Col1 мы можем напрямую создавать фиктивные переменные, используя pd.get_dummies()
и сохранить его в другом фрейме col1_df
, Мы можем объединить оба столбца, используя pd.concat([df,col1_df], axis=1, sort=False)
Я почти уверен, что вы столкнулись с проблемами с памятью, потому что str.get_dummies возвращает массив, полный 1 и 0, с типом данных np.int64
, Это сильно отличается от поведения pd.get_dummies, который возвращает массив значений типа данных uint8
,
Похоже, это известная проблема. Однако за прошедший год не было ни обновлений, ни исправлений. Проверка исходного кода для str.get_dummies действительно подтвердит, что он возвращается np.int64
,
8-разрядное целое число займет 1 байт памяти, а 64-разрядное целое - 8 байтов. Я надеюсь, что проблем с памятью можно избежать, найдя альтернативный способ быстрого кодирования Col2
который обеспечивает вывод всех 8-битных целых чисел.
Вот мой подход, начиная с вашего примера:
df = pd.DataFrame({'Col1': ['X', 'Y', 'X'],
'Col2': ['a,b,c', 'a,b', 'b,d']})
df
Col1 Col2
0 X a,b,c
1 Y a,b
2 X b,d
- поскольку
Col1
содержит простые строки без разделителей, мы можем легко их кодировать одним нажатием, используя pd.get_dummies:
df = pd.get_dummies(df, columns=['Col1'])
df
Col2 Col1_X Col1_Y
0 a,b,c 1 0
1 a,b 0 1
2 b,d 1 0
Все идет нормально.
df['Col1_X'].values.dtype
dtype('uint8')
- Давайте получим список всех уникальных подстрок, содержащихся внутри строк, разделенных запятыми, в
Col2
:
vals = list(df['Col2'].str.split(',').values)
vals = [i for l in vals for i in l]
vals = list(set(vals))
vals.sort()
vals
['a', 'b', 'c', 'd']
- Теперь мы можем перебрать приведенный выше список значений и использовать
str.contains
создать новый столбец для каждого значения, например'a'
, Каждая строка в новом столбце будет содержать 1, если эта строка действительно имеет значение нового столбца, например'a'
внутри его строки вCol2
, Когда мы создаем каждый новый столбец, мы конвертируем его тип данных вuint8
:
col='Col2'
for v in vals:
n = col + '_' + v
df[n] = df[col].str.contains(v)
df[n] = df[n].astype('uint8')
df.drop(col, axis=1, inplace=True)
df
Col1_X Col1_Y Col2_a Col2_b Col2_c Col2_d
0 1 0 1 1 1 0
1 0 1 1 1 0 0
2 1 0 0 1 0 1
Это приводит к кадру данных, который соответствует вашему желаемому формату. И, к счастью, целые числа в четырех новых столбцах, которые были горячо закодированы из Col2
занимать только 1 байт каждый, в отличие от 8 байт каждый.
df['Col2_a'].dtype
dtype('uint8')
Если, с внешней стороны, вышеуказанный подход не работает. Мой совет будет использовать str.get_dummies для быстрого кодирования Col2
в кусках рядов. Каждый раз, когда вы делаете чанк, вы конвертируете его тип данных из np.int64
в uint8
, а затем преобразовать блок в разреженную матрицу. Вы можете в конечном итоге объединить все куски обратно вместе.