извлечение серии фрагментов заголовков столбцов на основе истинности значений фрейма данных

#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