Преобразование столбцов списков pandas в матричное представление (одно горячее кодирование)

#python #pandas #list

#python #pandas #Список

Вопрос:

У меня есть столбец pandas со списками значений различной длины, например:

   idx lists

    0 [1,3,4,5]
    1 [2]
    2 [3,5]
    3 [2,3,5]
  

Я хотел бы преобразовать их в матричный формат, где каждое возможное значение представляет столбец, и каждая строка заполняет 1, если значение существует, и 0 в противном случае, вот так:

 idx  1 2 3 4 5 

  0  1 0 1 1 1
  1  0 1 0 0 0
  2  0 0 1 0 1
  3  0 1 1 0 1
  

Я думал, что термин для этого — одно горячее кодирование, но я попытался использовать метод pd.get_dummies, в котором указано, что он может выполнять одно горячее кодирование, но когда я пытаюсь ввести ввод, как показано выше:

 test_hot = pd.Series([[1,2,3],[3,4,5],[1,6]])
pd.get_dummies(test_hot)
  

Я получаю следующую ошибку:

 Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/lib/python3.7/site-packages/pandas/core/reshape/reshape.py", line 899, in get_dummies
    dtype=dtype)
  File "/opt/anaconda3/lib/python3.7/site-packages/pandas/core/reshape/reshape.py", line 906, in _get_dummies_1d
    codes, levels = _factorize_from_iterable(Series(data))
  File "/opt/anaconda3/lib/python3.7/site-packages/pandas/core/arrays/categorical.py", line 2515, in _factorize_from_iterable
    cat = Categorical(values, ordered=True)
  File "/opt/anaconda3/lib/python3.7/site-packages/pandas/core/arrays/categorical.py", line 347, in __init__
    codes, categories = factorize(values, sort=False)
  File "/opt/anaconda3/lib/python3.7/site-packages/pandas/util/_decorators.py", line 178, in wrapper
    return func(*args, **kwargs)
  File "/opt/anaconda3/lib/python3.7/site-packages/pandas/core/algorithms.py", line 630, in factorize
    na_value=na_value)
  File "/opt/anaconda3/lib/python3.7/site-packages/pandas/core/algorithms.py", line 476, in _factorize_array
    na_value=na_value)
  File "pandas/_libs/hashtable_class_helper.pxi", line 1601, in pandas._libs.hashtable.PyObjectHashTable.get_labels
TypeError: unhashable type: 'list'
  

Метод отлично работает, если я передаю один список значений, таких как:

 [1,2,3,4,5]
  

Он покажет матрицу 5×5, но заполняет только одну строку с 1. Я пытаюсь расширить это, чтобы в каждой строке можно было заполнить более 1 значения путем ввода столбца списков.

Ответ №1:

Если важна производительность, используйте MultiLabelBinarizer :

 test_hot = pd.Series([[1,2,3],[3,4,5],[1,6]])

from sklearn.preprocessing import MultiLabelBinarizer

mlb = MultiLabelBinarizer()
df = pd.DataFrame(mlb.fit_transform(test_hot),columns=mlb.classes_)
print (df)
   1  2  3  4  5  6
0  1  1  1  0  0  0
1  0  0  1  1  1  0
2  1  0  0  0  0  1
  

Ваше решение должно быть изменено с помощью create DataFrame , reshape и DataFrame.stack , в последний get_dummies раз, с помощью DataFrame.max for aggregate:

 df = pd.get_dummies(pd.DataFrame(test_hot.values.tolist()).stack().astype(int))
       .max(level=0, axis=0)

print (df)
   1  2  3  4  5  6
0  1  1  1  0  0  0
1  0  0  1  1  1  0
2  1  0  0  0  0  1
  

Подробные сведения:

Создано MultiIndex Series :

 print(pd.DataFrame(test_hot.values.tolist()).stack().astype(int))
0  0    1
   1    2
   2    3
1  0    3
   1    4
   2    5
2  0    1
   1    6
dtype: int32
  

Вызов pd.get_dummies :

 print (pd.get_dummies(pd.DataFrame(test_hot.values.tolist()).stack().astype(int)))
     1  2  3  4  5  6
0 0  1  0  0  0  0  0
  1  0  1  0  0  0  0
  2  0  0  1  0  0  0
1 0  0  0  1  0  0  0
  1  0  0  0  1  0  0
  2  0  0  0  0  1  0
2 0  1  0  0  0  0  0
  1  0  0  0  0  0  1
  

И последний агрегат max для первого уровня.

Комментарии:

1. Мне любопытно, как вы узнали, как изменять данные до вызова get_dummies, только из собственного опыта взлома кода или вы мастер? Я не видел ничего, что упоминало бы эти шаги в документации. Я спрашиваю, потому что я расстраиваюсь, пробуя несколько вещей и натыкаясь на стену, поэтому JW, если есть какой-то другой источник, который я должен использовать, чтобы изучить, как реализовать мой код.

2. @BenCWang — хм, почему не в официальных документах, это особый способ — списки последовательно, что плохо поддерживается. Итак, решение создается Series с помощью скаляров, а не вложенных списков, а затем вызывается pd.get_dummies .

Ответ №2:

Исправляя свой get_dummies код, вы можете использовать:

 df['lists'].map(lambda x: ','.join(map(str, x))).str.get_dummies(sep=',')

   1  2  3  4  5
0  1  0  1  1  1
1  0  1  0  0  0
2  0  0  1  0  1
3  0  1  1  0  1
  

Комментарии:

1. да, только медленно: (

2. @jezrael наши ответы не совпадают?

3. Возможно, @jezrael, но его удобно использовать с sep аргументом

4. тогда лучше df['lists'].map(lambda x: '|'.join(map(str, x))).str.get_dummies()