web-dev-qa-db-ja.com

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=????)

しかし、グループ化されたデータフレームをどのように指定する必要がありますか(つまり、DFcontraster?)

19
nanounanue

少し当て推量で、次のことが求められていると思います。

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でグループ化するため、各グループには、それ自体とのみ一致する1つのアイテムのみが含まれます。

ただし、複数の行にある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_nameexactの一致が必要ない場合は、first_nameでインデックスを並べ替え/設定して使用する必要があります。 map_partitions 似たような方法で。その場合、あなたはあなたの質問を改革する必要があるでしょう。

4
mdurant

Groupby-applyに提供する関数は、入力としてPandasデータフレームまたはシリーズを取り、理想的には出力として1つ(またはスカラー値)を返す必要があります。追加のパラメーターは問題ありませんが、セカンダリである必要があります。最初の引数ではありません。これは、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
9
MRocklin