Разностная разница в двух списках в пандах - PullRequest
0 голосов
/ 28 октября 2018

Я использую pandas, чтобы постепенно находить новые элементы, т. Е. Для каждой строки я буду видеть, были ли значения в списке видны ранее.Если они есть, мы будем игнорировать их.Если нет, мы их выберем.

Я смог сделать это, используя row.iterrows(), но у меня> 1M строк, поэтому я считаю, что векторизация apply может быть лучше.

Вот пример данных и кода.Запустив этот код, вы получите ожидаемый результат:

from numpy import nan as NA
import collections

df = pd.DataFrame({'ID':['A','B','C','A','B','A','A','A','D','E','E','E'],
                   'Value': [1,2,3,4,3,5,2,3,7,2,3,9]})
#wrap all elements by group in a list
Changed_df=df.groupby('ID')['Value'].apply(list).reset_index() 
Changed_df=Changed_df.rename(columns={'Value' : 'Elements'})
Changed_df=Changed_df.reset_index(drop=True)



def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el

Changed_df["Elements_s"]=Changed_df['Elements'].shift()

#attempt 1: For loop
Changed_df["Diff"]=NA
Changed_df["count"]=0
Elements_so_far = []

#replace NA with empty list in columns that will go through list operations
for col in ["Elements","Elements_s","Diff"]:
    Changed_df[col] = Changed_df[col].apply(lambda d: d if isinstance(d, list) else [])

for idx,row in Changed_df.iterrows():
    diff = list(set(row['Elements']) - set(Elements_so_far))
    Changed_df.at[idx, "Diff"] = diff
    Elements_so_far.append(row['Elements'])
    Elements_so_far = flatten(Elements_so_far)
    Elements_so_far = list(set(Elements_so_far)) #keep unique elements
    Changed_df.loc[idx,"count"]=diff.__len__()

Комментарий к коду:

  • Я не фанат этого кода, потому чтоэто неуклюже и неэффективно.
    • Я говорю неэффективно, потому что я создал Elements_s, который содержит сдвинутые значения.Другой причиной неэффективности является for цикл по строкам.
  • Elements_so_far отслеживает все элементы, которые мы обнаружили для каждой строки.Если появляется новый элемент, который отображается, мы считаем это в столбце Diff.
  • Мы также отслеживаем длину новых элементов, обнаруженных в столбце count.

Я был бы признателен, если бы эксперт мог помочь мне с векторной версией кода.


Я попробовал векторизованную версию, но я не мог зайти слишком далеко.

#attempt 2:
Changed_df.apply(lambda x: [i for i in x['Elements'] if i in x['Elements_s']], axis=1)

Меня вдохновили Как сравнить два столбца со списком строк и создать новый столбец с уникальными элементами? сделать выше, но я не смог этого сделать.Связанный поток SO выполняет построчное различие между столбцами.

Я использую Python 3.6.7 от Anaconda.Версия для панд - 0.23.4

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Одна альтернатива с использованием drop duplicates и groupby

# Groupby and apply list func.
df1 = df.groupby('ID')['Value'].apply(list).to_frame('Elements')

# Sort values , drop duplicates by Value column then use groupby.
df1['Diff'] = df.sort_values(['ID','Value']).drop_duplicates('Value').groupby('ID')['Value'].apply(list)

# Use str.len for count.
df1['Count'] = df1['Diff'].str.len().fillna(0).astype(int)

# To fill NaN with empty list
df1['Diff'] = df1.Diff.apply(lambda x: x if type(x)==list else []) 


           Elements             Diff   Count
ID               
A   [1, 4, 5, 2, 3]  [1, 2, 3, 4, 5]     5
B            [2, 3]               []     0
C               [3]               []     0
D               [7]              [7]     1
E         [2, 3, 9]              [9]     1
0 голосов
/ 28 октября 2018

Вы можете использовать sort, а затем использовать numpy, чтобы получить индексы unique, а затем построить свои группировки, например:

In []:
df = df.sort_values(by='ID').reset_index(drop=True)
_, i = np.unique(df.Value.values, return_index=True)
df.iloc[i].groupby(df.ID).Value.apply(list)

Out[]:
ID
A    [1, 2, 3, 4, 5]
D                [7]
E                [9]
Name: Value, dtype: object

Или приблизиться к текущему выводу:

In []:
df = df.sort_values(by='ID').reset_index(drop=True)
_, i = np.unique(df.Value.values, return_index=True)
s1 = df.groupby(df.ID).Value.apply(list).rename('Elements')
s2 = df.iloc[i].groupby(df.ID).Value.apply(list).rename('Diff').reindex(s1.index, fill_value=[])

pd.concat([s1, s2, s2.apply(len).rename('Count')], axis=1)

Out[]:
           Elements             Diff  Count
ID
A   [1, 4, 5, 2, 3]  [1, 2, 3, 4, 5]      5
B            [2, 3]               []      0
C               [3]               []      0
D               [7]              [7]      1
E         [2, 3, 9]              [9]      1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...