Как горячим образом кодировать кадр данных pandas для целых столбцов, а не для каждого столбца? - PullRequest
3 голосов
/ 16 мая 2019

Я хочу горячим образом кодировать кадр данных pandas для целых столбцов, а не для каждого столбца.

Если есть кадр данных, как показано ниже:

df = pd.DataFrame({'A': ['A1', 'A1', 'A1', 'A1', 'A4', 'A5'], 'B': ['A2', 'A2', 'A2', 'A3', np.nan, 'A6], 'C': ['A4', 'A3', 'A3', 'A5', np.nan, np.nan]})
df =
        A    B   C
    0  A1   A2  A4
    1  A1   A2  A3
    2  A1   A2  A3
    3  A1   A3  A5
    4  A4   NaN NaN
    5  A5   A6  NaN

Я хочу закодировать его, как показано ниже:

    df =
            A1    A2   A3   A4   A5   A6
        0    1     1    0    1    0    0
        1    1     1    1    0    0    0
        2    1     1    1    0    0    0
        3    1     0    1    0    1    0
        4    0     0    0    1    0    0
        5    0     0    0    0    1    1

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

df = pd.get_dummies(df, sparse=True)
df = 
   A_A1  A_A4  A_A5  B_A2  B_A3  B_A6  C_A3  C_A4  C_A5
0     1     0     0     1     0     0     0     1     0
1     1     0     0     1     0     0     1     0     0
2     1     0     0     1     0     0     1     0     0
3     1     0     0     0     1     0     0     0     1
4     0     1     0     0     0     0     0     0     0
5     0     0     1     0     0     1     0     0     0

Как мне выполнить горячее кодирование для целых столбцов? Если я использую префикс = '', он также создает столбцы, такие как _A1 _A4 _A5 _A2 _A3 _A6 _A3 _A4 _A5. (Я надеюсь создать код, используя библиотеку pandas или numpy, а не наивный цикл for-loop, потому что мои данные очень большие; 16000000 строк, поэтому итеративный наивный код for-loop потребует длительного времени вычисления).

Ответы [ 2 ]

4 голосов
/ 16 мая 2019

В вашем случае

df.stack().str.get_dummies().sum(level=0)
Out[116]: 
   A1  A2  A3  A4  A5  A6
0   1   1   0   1   0   0
1   1   1   1   0   0   0
2   1   1   1   0   0   0
3   1   0   1   0   1   0
4   0   0   0   1   0   0
5   0   0   0   0   1   1

Или исправьте pd.get_dummies с помощью prefix

pd.get_dummies(df, prefix='',prefix_sep='').sum(level=0,axis=1)
Out[118]: 
   A1  A4  A5  A2  A3  A6
0   1   1   0   1   0   0
1   1   0   0   1   1   0
2   1   0   0   1   1   0
3   1   0   1   0   1   0
4   0   1   0   0   0   0
5   0   0   1   0   0   1
3 голосов
/ 16 мая 2019

Quicker

# Pandas 0.24 or greater use `.to_numpy()` instead of `.values`
v = df.values
n, m = v.shape
j, cols = pd.factorize(v.ravel())  # -1 when `np.nan`

# Used to grab only non-null values
mask = j >= 0
i = np.arange(n).repeat(m)[mask]
j = j[mask]

out = np.zeros((n, len(cols)), dtype=int)
# Useful when not one-hot.  Otherwise use `out[i, j] = 1`
np.add.at(out, (i, j), 1)

pd.DataFrame(out, df.index, cols)

   A1  A2  A4  A3  A5  A6
0   1   1   1   0   0   0
1   1   1   0   1   0   0
2   1   1   0   1   0   0
3   1   0   0   1   1   0
4   0   0   1   0   0   0
5   0   0   0   0   1   1

Не быстрее

Это сделано для того, чтобы показать, что вы можете объединить значения строк, а затем использовать str.get_dummies

df.stack().groupby(level=0).apply('|'.join).str.get_dummies()

   A1  A2  A3  A4  A5  A6
0   1   1   0   1   0   0
1   1   1   1   0   0   0
2   1   1   1   0   0   0
3   1   0   1   0   1   0
4   0   0   0   1   0   0
5   0   0   0   0   1   1

sklearn

from sklearn.preprocessing import MultiLabelBinarizer as MLB

mlb = MLB()
out = mlb.fit_transform([[*filter(pd.notna, x)] for x in zip(*map(df.get, df))])

pd.DataFrame(out, df.index, mlb.classes_)

   A1  A2  A3  A4  A5  A6
0   1   1   0   1   0   0
1   1   1   1   0   0   0
2   1   1   1   0   0   0
3   1   0   1   0   1   0
4   0   0   0   1   0   0
5   0   0   0   0   1   1
...