Преобразование категориальных данных в набор пустышек - PullRequest
0 голосов
/ 13 февраля 2019

У меня есть такие данные: -

|--------|---------|
| 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, используя this , но получая MemoryError, так как данные велики, и в col2 также много изменений.

Итак,

1) Как я могу преобразовать несколько категориальных столбцов в фиктивную переменную?

2) pandas get_dummy () выдает ошибку памяти, так как я могу это исправить?

Ответы [ 2 ]

0 голосов
/ 14 февраля 2019

Я бы тоже хотел дать свое решение.И я хотел бы поблагодарить @ 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)

0 голосов
/ 14 февраля 2019

Я почти уверен, что вы столкнулись с проблемами с памятью, потому что str.get_dummies возвращает массив, полный единиц и 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
  1. Так как 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, а затем преобразует чанк в разреженную матрицу .В конечном итоге вы можете объединить все куски вместе.

...