Применить функцию к сгруппированному фрейму данных в Dask: Как указать сгруппированный фрейм данных в качестве аргумента в функции?
у меня есть 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
в реальной жизни-это несколько тысяч строк)
я хочу применить пользовательскую функцию к каждому сгруппированному фрейму данных. Я хочу сравнить каждую строку с другими строками в группе (что-то вроде панды сравнивают каждую строку со всеми строками в фрейме данных и сохраняют результаты в списке для каждой строки).
следующая функция, которую я пытаюсь чтобы применить:
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
in contraster
?)
2 ответов
С небольшим количеством догадок, я думаю, что следующее-Это то, что вам нужно.
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]
т. е., потому что вы группируетесь по "имя", каждая группа содержит только один элемент, который соответствует только себе.
Если, однако, у вас есть некоторые "имя" значения, которые были в нескольких строках, вы получите совпадения:
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
аналогично. В таком случае вам придется пересмотреть свой вопрос.
функция, которую вы предоставляете groupby-apply, должна принимать фрейм данных или серию Pandas в качестве входных данных и в идеале возвращать одно (или скалярное значение) в качестве выходных данных. Дополнительные параметры в порядке, но они должны быть вторичными, а не первым аргументом. Это то же самое как в панд и dask dataframe.
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()
это приведет к тому же выходу в панд или ДАСК.таблицы данных
count first_name x
0 2 Alice 3
1 2 Alice 3
2 1 Bob 3