Панды: Подмножество данных, используя список, сохраняя дубликаты несколько раз - PullRequest
1 голос
/ 07 марта 2019

У меня большой список, который содержит повторяющиеся значения, и я хочу установить подкадр данных, используя значения списка.Обычно я бы использовал метод .isin, но я хочу сохранить повторяющиеся строки.Вот пример кода:

df = pd.DataFrame(np.array([[1, 2, 'car'], [4, 5, 'bike'], [1, 2, 'train'], [1, 2, 'car'], [1, 2, 'train']]),columns=['a', 'b', 'c'])

lst = ['car', 'bike', 'car', 'car']

Поэтому я хочу возвращать фрейм данных, который включает все строки каждый раз, когда они происходят.Каждый раз, когда элемент появляется в списке, я хочу вернуть соответствующие строки.

В простом наборе данных, таком как приведенный выше, я могу циклически проходить по списку и добавлять к новому фрейму данных возвращаемые значения, но побольшой набор данных, кажется, занимает очень много времени.Любые предложения?

РЕДАКТИРОВАТЬ: Таким образом, предложение Криса работает, и обеспечивает ожидаемый результат, используя:

pd.concat([df[df['c'].eq(x)] for x in lst])

Однако, как и при использовании цикла это очень медленно по сравнению с чем-то вроде.isin метод при работе с гораздо большими данными.Добавлена ​​эта правка, чтобы можно было создать ожидаемый вывод.

Ответы [ 3 ]

1 голос
/ 07 марта 2019

Первый шаг - фильтрация только совпадающих значений:

df = df[df['c'].isin(lst)]

Затем сгладьте значения индекса, которые соответствуют условию, и затем используйте loc для повторения, np.repeat также возможно, но оно преобразует все столбцы в строки, поэтому здесь невозможно использовать:

idx = [y for x in lst for y in df.index[df['c'].values == x]]
df_new = df.loc[idx].reset_index(drop=True)
print (df_new)
   a  b     c
0  1  2   car
1  1  2   car
2  4  5  bike
3  1  2   car
4  1  2   car
5  1  2   car
6  1  2   car

Если много повторяющихся значений возможно, отфильтруйте только один раз, а затем повторите значения индекса:

ser = pd.Series(lst)
idx = ser.map({k:df.index[df['c'].values == k] for k, v in ser.value_counts().items()})
df_new = df.loc[list(chain.from_iterable(idx))].reset_index(drop=True)
print (df_new)
   a  b     c
0  1  2   car
1  1  2   car
2  4  5  bike
3  1  2   car
4  1  2   car
5  1  2   car
6  1  2   car

Другое решение:

from  itertools import chain
from collections import Counter

d = {k:df.index[df['c'].values == k] for k, v in Counter(lst).items()}
idx = [y for x in lst for y in d[x]]
df_new = df.loc[idx].reset_index(drop=True)
1 голос
/ 07 марта 2019

IIUC, используйте pandas.concat с пониманием списка:

df_new = pd.concat([df[df['c'].eq(x)] for x in lst], ignore_index=True)

Альтернативным подходом может быть создание вспомогательного Series с методом value_counts в вашем списке и сокращение исходной фильтрации DataFrame с использованием метода .isin:

s = pd.Series(lst).value_counts()
df = df[df['c'].isin(set(lst))]

idx = np.concatenate([df[df['c'].eq(i)].index.repeat(r) for i, r in s.iteritems()])

df_new = df.loc[idx]
0 голосов
/ 07 марта 2019

Если я правильно понял ваш вопрос, groupby может помочь:

gr = df.groupby('c')
for i in lst:
    subset = gr.get_group(i)
    # process subset...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...