#python #pandas #numpy
#python #pandas #numpy
Вопрос:
рассмотрим фрейм данных df
np.random.seed([3,1415])
df = pd.DataFrame(np.random.choice((0, 1), (3, 3)),
columns=['blah', 'meep', 'zimp'])
df
вопрос
каков наиболее эффективный способ нарезки df.columns
с каждой строкой df
?
(для этого примера и в масштабе)
ожидаемые результаты
0 [meep]
1 [blah]
2 [blah, zimp]
dtype: object
В масштабе
Я подтвердил, что @jezrael, @boud и мой ответ дают одинаковые результаты. Ниже приведен фрейм данных, который я использовал для проверки масштаба каждого решения
from string import letters
import pandas as pd
import numpy as np
mux = pd.MultiIndex.from_product([list(letters), list(letters)])
df = pd.DataFrame(np.arange(52 ** 4).reshape(52 ** 2, -1) % 3 % 2, mux, mux)
настройка для boud
s = pd.Series([[x] for x in df], df.columns)
настройка для pirsquared
num = df.columns.nlevels
lvls = list(range(num))
rlvls = [x * -1 - 1 for x in lvls]
xsl = lambda x: x.xs(x.name).index.tolist()
Результаты
маленький df
Ответ №1:
Вы можете использовать mul
с list comprehension
:
df = df.mul(df.columns.to_series(), axis=1)
print (df)
blah meep zimp
0 meep
1 blah
2 blah zimp
print ([list(filter(None, x)) for x in df.values.tolist()])
[['meep'], ['blah'], ['blah', 'zimp']]
print (pd.Series([list(filter(None, x)) for x in df.values.tolist()], index=df.index))
0 [meep]
1 [blah]
2 [blah, zimp]
dtype: object
Комментарии:
1. Хммм, я считаю
groupby
, что решение работает очень медленно… И, к сожалению, я не могу добавить тайминги сейчас2. Спасибо, интересно. Я думал, что какое-то другое решение может быть быстрее.
Ответ №2:
Я предлагаю использовать dot
после построения серии атомарных списков:
s = pd.Series([[col] for col in df.columns])
s.index = df.columns
df.dot(s)
Out[35]:
0 [meep]
1 [blah]
2 [blah, zimp]
dtype: object
Комментарии:
1. Это как раз одно из моих умных решений 😉
2. Более того, вы были моей мотивацией для того, чтобы придумать это
Ответ №3:
Другое решение, использующее сумму продуктов np.sum
, за которой следует, str.split
как показано:
sep = ' '
pd.Series((df.values*(df.columns.values sep)).sum(1)).str.split()
0 [meep]
1 [blah]
2 [blah, zimp]
dtype: object
Комментарии:
1. отличный ответ! не обобщается на мультииндекс.
2. Спасибо. Да, я думаю, это было сделано специально для
DF
предоставленного вами образца. Никогда не думал о возникновении ситуации с несколькими индексами 🙂
Ответ №4:
используйте where
и stack
для удаления 0
s, затем захватите оставшиеся индексы
# number of levels in columns
num = df.columns.nlevels
# handy list for stacking
lvls = list(range(num))
# reverse (sort of) list for unstacking
rlvls = [x * -1 - 1 for x in lvls]
# get just levels in index that used to be columns
xsl = lambda x: x.xs(x.name).index.tolist()
# where is faster than replace
# when I stack, I'll drop all np.nan
# then just grab the indices that are left
df.where(df, np.nan).stack(lvls).groupby(level=lvls).apply(xsl)
0 [meep]
1 [blah]
2 [blah, zimp]
dtype: object