Применить функцию к сгруппированному фрейму данных в Dask: как вы определяете сгруппированный Dataframe как аргумент в функции?
У меня есть dask dataframe
сгруппированный по индексу (first_name
).
import pandas as pd
import numpy as np
from multiprocessing import cpu_count
from dask import dataframe as dd
from dask.multiprocessing import get
from dask.distributed import Client
NCORES = cpu_count()
client = Client()
entities = pd.DataFrame({'first_name':['Jake','John','Danae','Beatriz', 'Jacke', 'Jon'],'last_name': ['Del Toro', 'Foster', 'Smith', 'Patterson', 'Toro', 'Froster'], 'ID':['X','U','X','Y', '12','13']})
df = dd.from_pandas(entities, npartitions=NCORES)
df = client.persist(df.set_index('first_name'))
(Очевидно, entities
в реальной жизни - несколько тысяч строк)
Я хочу применить определенную пользователем функцию к каждому сгруппированному кадру данных. Я хочу сравнить каждую строку со всеми остальными строками в группе (что-то похожее на Pandas, сравнивает каждую строку со всеми строками в кадре данных и сохраняет результаты в списке для каждой строки).
Следующей является функция, которую я пытаюсь применить:
def contraster(x, DF):
matches = DF.apply(lambda row: fuzz.partial_ratio(row['last_name'], x) >= 50, axis = 1)
return [i for i, x in enumerate(matches) if x]
Для кадра данных тестовых entities
вы можете применить функцию как обычно:
entities.apply(lambda row: contraster(row['last_name'], entities), axis =1)
И ожидаемый результат:
Out[35]:
0 [0, 4]
1 [1, 5]
2 [2]
3 [3]
4 [0, 4]
5 [1, 5]
dtype: object
Когда entities
огромны, в решении используется использование dask
. Обратите внимание, что DF
в функции contraster
должен быть групповым файловым кадром.
Я пытаюсь использовать следующее:
df.groupby('first_name').apply(func=contraster, args=????)
Но как мне указать сгруппированный фрейм данных (т.е. DF
в contraster
?)
Ответы
Ответ 1
С небольшим количеством догадок, я думаю, что следующее, что вы после.
def mapper(d):
def contraster(x, DF=d):
matches = DF.apply(lambda row: fuzz.partial_ratio(row['last_name'], x) >= 50, axis = 1)
return [d.ID.iloc[i] for i, x in enumerate(matches) if x]
d['out'] = d.apply(lambda row:
contraster(row['last_name']), axis =1)
return d
df.groupby('first_name').apply(mapper).compute()
Применительно к вашим данным, вы получаете:
ID first_name last_name out
2 X Danae Smith [X]
4 12 Jacke Toro [12]
0 X Jake Del Toro [X]
1 U John Foster [U]
5 13 Jon Froster [13]
3 Y Beatriz Patterson [Y]
т.е. потому, что вы группируете по первому имени, каждая группа содержит только один элемент, который соответствует только самому себе.
Если, однако, у вас есть некоторые значения first_name, которые были в нескольких строках, вы получите совпадения:
entities = pd.DataFrame(
{'first_name':['Jake','Jake', 'Jake', 'John'],
'last_name': ['Del Toro', 'Toro', 'Smith'
'Froster'],
'ID':['Z','U','X','Y']})
Выход:
ID first_name last_name out
0 Z Jake Del Toro [Z, U]
1 U Jake Toro [Z, U]
2 X Jake Smith [X]
3 Y John Froster [Y]
Если вам не нужны точные совпадения для first_name, то, возможно, вам нужно отсортировать/установить индекс по map_partitions
имени и использовать map_partitions
аналогичным образом. В этом случае вам нужно будет реформировать свой вопрос.
Ответ 2
Функция, которую вы предоставляете groupby-apply, должна принимать в качестве входного кадра или серии Pandas данные и в идеале возвращать один (или скалярное значение) в качестве вывода. Дополнительные параметры прекрасны, но они должны быть вторичными, а не первым аргументом. Это то же самое, что и в кадре данных Pandas и Dask.
def func(df, x=None):
# do whatever you want here
# the input to this function will have all the same first name
return pd.DataFrame({'x': [x] * len(df),
'count': len(df),
'first_name': df.first_name})
Затем вы можете вызвать df.groupby как обычно
import pandas as pd
import dask.dataframe as dd
df = pd.DataFrame({'first_name':['Alice', 'Alice', 'Bob'],
'last_name': ['Adams', 'Jones', 'Smith']})
ddf = dd.from_pandas(df, npartitions=2)
ddf.groupby('first_name').apply(func, x=3).compute()
Это даст тот же результат в pandas или dask.dataframe
count first_name x
0 2 Alice 3
1 2 Alice 3
2 1 Bob 3