Pandas преобразовать столбец списка в макеты
У меня есть dataframe, где один столбец представляет собой список групп, к которым принадлежит каждый из моих пользователей. Что-то вроде:
index groups
0 ['a','b','c']
1 ['c']
2 ['b','c','e']
3 ['a','c']
4 ['b','e']
И то, что я хотел бы сделать, это создать серию фиктивных столбцов, чтобы определить, к каким группам принадлежит каждый пользователь, чтобы выполнить некоторые анализы.
index a b c d e
0 1 1 1 0 0
1 0 0 1 0 0
2 0 1 1 0 1
3 1 0 1 0 0
4 0 1 0 0 0
pd.get_dummies(df['groups'])
не будет работать, потому что это просто возвращает столбец для каждого другого списка в моем столбце.
Решение должно быть эффективным, так как dataframe будет содержать 500 000 строк. Любые советы будут оценены!
Ответы
Ответ 1
Используя s
для вашего df['groups']
:
In [21]: s = pd.Series({0: ['a', 'b', 'c'], 1:['c'], 2: ['b', 'c', 'e'], 3: ['a', 'c'], 4: ['b', 'e'] })
In [22]: s
Out[22]:
0 [a, b, c]
1 [c]
2 [b, c, e]
3 [a, c]
4 [b, e]
dtype: object
Это возможное решение:
In [23]: pd.get_dummies(s.apply(pd.Series).stack()).sum(level=0)
Out[23]:
a b c e
0 1 1 1 0
1 0 0 1 0
2 0 1 1 1
3 1 0 1 0
4 0 1 0 1
Логика этого:
-
.apply(Series)
преобразует серию списков в dataframe
-
.stack()
снова помещает все в один столбец (создание многоуровневого индекса)
-
pd.get_dummies( )
создание манекенов
-
.sum(level=0
) для объединения разных строк, которые должны быть одной строкой (путем суммирования второго уровня, сохраняя только исходный уровень (level=0
))
Небольшой эквивалент pd.get_dummies(s.apply(pd.Series), prefix='', prefix_sep='').sum(level=0, axis=1)
Если это будет достаточно эффективно, я не знаю, но в любом случае, если производительность важна, сохранение списков в фрейме данных не очень хорошая идея.
Ответ 2
Несмотря на то, что на этот квест был дан ответ, у меня есть более быстрое решение:
df.groups.apply(lambda x: pd.Series([1] * len(x), index=x)).fillna(0, downcast='infer')
И, если у вас есть пустые группы или NaN
, вы можете просто:
df.loc[df.groups.str.len() > 0].apply(lambda x: pd.Series([1] * len(x), index=x)).fillna(0, downcast='infer')
Как это работает
Внутри лямбда x
- ваш список, например ['a', 'b', 'c']
. Итак, pd.Series
будет выглядеть следующим образом:
In [2]: pd.Series([1, 1, 1], index=['a', 'b', 'c'])
Out[2]:
a 1
b 1
c 1
dtype: int64
Когда все pd.Series
объединяются, они становятся pd.DataFrame
, а их index
становятся columns
; missing index
стал column
с NaN
, как вы можете видеть далее:
In [4]: a = pd.Series([1, 1, 1], index=['a', 'b', 'c'])
In [5]: b = pd.Series([1, 1, 1], index=['a', 'b', 'd'])
In [6]: pd.DataFrame([a, b])
Out[6]:
a b c d
0 1.0 1.0 1.0 NaN
1 1.0 1.0 NaN 1.0
Теперь fillna
заполняет те NaN
0
:
In [7]: pd.DataFrame([a, b]).fillna(0)
Out[7]:
a b c d
0 1.0 1.0 1.0 0.0
1 1.0 1.0 0.0 1.0
И downcast='infer'
состоит в том, чтобы сбрасывать от float
до int
:
In [11]: pd.DataFrame([a, b]).fillna(0, downcast='infer')
Out[11]:
a b c d
0 1 1 1 0
1 1 1 0 1
PS: Не требуется использование .fillna(0, downcast='infer')
.