105

列に基づいてマージしたい2つのDataFrameがあります。ただし、つづりの違い、スペースの数の違い、発音区別符号の有無などにより、類似している限りマージできるようにしたいと思います。

任意の類似性アルゴリズム(soundex、Levenshtein、difflib)で実行できます。

1つのDataFrameに次のデータがあるとします。

df1 = DataFrame([[1],[2],[3],[4],[5]], index=['one','two','three','four','five'], columns=['number'])

       number
one         1
two         2
three       3
four        4
five        5

df2 = DataFrame([['a'],['b'],['c'],['d'],['e']], index=['one','too','three','fours','five'], columns=['letter'])

      letter
one        a
too        b
three      c
fours      d
five       e

次に、結果のDataFrameを取得します

       number letter
one         1      a
two         2      b
three       3      c
four        4      d
five        5      e
4

13 に答える 13

100

@locojayの提案と同様に、difflib'sget_close_matchesdf2'sインデックスに適用してから、join:を適用できます。

In [23]: import difflib 

In [24]: difflib.get_close_matches
Out[24]: <function difflib.get_close_matches>

In [25]: df2.index = df2.index.map(lambda x: difflib.get_close_matches(x, df1.index)[0])

In [26]: df2
Out[26]: 
      letter
one        a
two        b
three      c
four       d
five       e

In [31]: df1.join(df2)
Out[31]: 
       number letter
one         1      a
two         2      b
three       3      c
four        4      d
five        5      e

これらが列の場合、同じように列に適用できますmerge

df1 = DataFrame([[1,'one'],[2,'two'],[3,'three'],[4,'four'],[5,'five']], columns=['number', 'name'])
df2 = DataFrame([['a','one'],['b','too'],['c','three'],['d','fours'],['e','five']], columns=['letter', 'name'])

df2['name'] = df2['name'].apply(lambda x: difflib.get_close_matches(x, df1['name'])[0])
df1.merge(df2)
于 2012-12-03T10:06:04.680 に答える
56

使用するfuzzywuzzy

パッケージには例がないためfuzzywuzzy、ユーザーとして設定できるしきい値に基づいてすべての一致を返す関数を作成しました。


datframeの例

df1 = pd.DataFrame({'Key':['Apple', 'Banana', 'Orange', 'Strawberry']})
df2 = pd.DataFrame({'Key':['Aple', 'Mango', 'Orag', 'Straw', 'Bannanna', 'Berry']})

# df1
          Key
0       Apple
1      Banana
2      Orange
3  Strawberry

# df2
        Key
0      Aple
1     Mango
2      Orag
3     Straw
4  Bannanna
5     Berry

あいまいマッチング機能

def fuzzy_merge(df_1, df_2, key1, key2, threshold=90, limit=2):
    """
    :param df_1: the left table to join
    :param df_2: the right table to join
    :param key1: key column of the left table
    :param key2: key column of the right table
    :param threshold: how close the matches should be to return a match, based on Levenshtein distance
    :param limit: the amount of matches that will get returned, these are sorted high to low
    :return: dataframe with boths keys and matches
    """
    s = df_2[key2].tolist()
    
    m = df_1[key1].apply(lambda x: process.extract(x, s, limit=limit))    
    df_1['matches'] = m
    
    m2 = df_1['matches'].apply(lambda x: ', '.join([i[0] for i in x if i[1] >= threshold]))
    df_1['matches'] = m2
    
    return df_1

データフレームでの関数の使用: #1

from fuzzywuzzy import fuzz
from fuzzywuzzy import process

fuzzy_merge(df1, df2, 'Key', 'Key', threshold=80)

          Key       matches
0       Apple          Aple
1      Banana      Bannanna
2      Orange          Orag
3  Strawberry  Straw, Berry

データフレームでの関数の使用: #2

df1 = pd.DataFrame({'Col1':['Microsoft', 'Google', 'Amazon', 'IBM']})
df2 = pd.DataFrame({'Col2':['Mcrsoft', 'gogle', 'Amason', 'BIM']})

fuzzy_merge(df1, df2, 'Col1', 'Col2', 80)

        Col1  matches
0  Microsoft  Mcrsoft
1     Google    gogle
2     Amazon   Amason
3        IBM         

インストール:

ピップ

pip install fuzzywuzzy

アナコンダ

conda install -c conda-forge fuzzywuzzy
于 2019-05-26T16:42:53.933 に答える
21

この問題を解決することを目的としたPythonパッケージを作成しました。

pip install fuzzymatcher

リポジトリはここにあり、ドキュメントはここにあります。

基本的な使用法:

df_leftファジー結合する2つのデータフレームとが与えられdf_rightた場合、次のように記述できます。

from fuzzymatcher import link_table, fuzzy_left_join

# Columns to match on from df_left
left_on = ["fname", "mname", "lname",  "dob"]

# Columns to match on from df_right
right_on = ["name", "middlename", "surname", "date"]

# The link table potentially contains several matches for each record
fuzzymatcher.link_table(df_left, df_right, left_on, right_on)

または、最も近い一致にリンクしたいだけの場合:

fuzzymatcher.fuzzy_left_join(df_left, df_right, left_on, right_on)
于 2017-12-02T09:15:43.890 に答える
13

Jaro-Winklerは、現在利用可能な最もパフォーマンスが高く正確な近似文字列マッチングアルゴリズムの1つであるため、使用します[ Cohen、etal。]、[ウィンクラー]。

これは、クラゲパッケージのジャロウィンクラーでそれを行う方法です。

def get_closest_match(x, list_strings):

  best_match = None
  highest_jw = 0

  for current_string in list_strings:
    current_score = jellyfish.jaro_winkler(x, current_string)

    if(current_score > highest_jw):
      highest_jw = current_score
      best_match = current_string

  return best_match

df1 = pandas.DataFrame([[1],[2],[3],[4],[5]], index=['one','two','three','four','five'], columns=['number'])
df2 = pandas.DataFrame([['a'],['b'],['c'],['d'],['e']], index=['one','too','three','fours','five'], columns=['letter'])

df2.index = df2.index.map(lambda x: get_closest_match(x, df1.index))

df1.join(df2)

出力:

    number  letter
one     1   a
two     2   b
three   3   c
four    4   d
five    5   e
于 2016-05-29T01:54:05.510 に答える
7

http://pandas.pydata.org/pandas-docs/dev/merging.htmlには、これをオンザフライで実行するためのフック機能がありません。でもいいでしょう...

別の手順を実行し、difflib getclosest_matchesを使用して、2つのデータフレームの1つに新しい列を作成し、あいまい一致列にマージ/結合します。

于 2012-11-30T19:56:02.180 に答える
6

一般的なアプローチの場合:fuzzy_merge

わずかに異なる文字列を含む2つのデータフレームの列をマージする、より一般的なシナリオの場合、次の関数は、パンダの機能を模倣するためにdifflib.get_close_matchesと一緒に使用しますが、あいまい一致を使用します。mergemerge

import difflib 

def fuzzy_merge(df1, df2, left_on, right_on, how='inner', cutoff=0.6):
    df_other= df2.copy()
    df_other[left_on] = [get_closest_match(x, df1[left_on], cutoff) 
                         for x in df_other[right_on]]
    return df1.merge(df_other, on=left_on, how=how)

def get_closest_match(x, other, cutoff):
    matches = difflib.get_close_matches(x, other, cutoff=cutoff)
    return matches[0] if matches else None

2つのサンプルデータフレームの使用例を次に示します。

print(df1)

     key   number
0    one       1
1    two       2
2  three       3
3   four       4
4   five       5

print(df2)

                 key_close  letter
0                    three      c
1                      one      a
2                      too      b
3                    fours      d
4  a very different string      e

上記の例では、次のようになります。

fuzzy_merge(df1, df2, left_on='key', right_on='key_close')

     key  number key_close letter
0    one       1       one      a
1    two       2       too      b
2  three       3     three      c
3   four       4     fours      d

そして、次のように左結合を行うことができます。

fuzzy_merge(df1, df2, left_on='key', right_on='key_close', how='left')

     key  number key_close letter
0    one       1       one      a
1    two       2       too      b
2  three       3     three      c
3   four       4     fours      d
4   five       5       NaN    NaN

右結合の場合、左側のデータフレームに一致しないすべてのキーがありますNone

fuzzy_merge(df1, df2, left_on='key', right_on='key_close', how='right')

     key  number                key_close letter
0    one     1.0                      one      a
1    two     2.0                      too      b
2  three     3.0                    three      c
3   four     4.0                    fours      d
4   None     NaN  a very different string      e

また、カットオフ内で一致するアイテムがない場合は、空のリストが返されることに注意してください。共有の例では、最後のインデックスを次のように変更すると、次のようになります。difflib.get_close_matches df2

print(df2)

                          letter
one                          a
too                          b
three                        c
fours                        d
a very different string      e

エラーが発生しますindex out of range

df2.index.map(lambda x: difflib.get_close_matches(x, df1.index)[0])

IndexError:リストインデックスが範囲外です

これを解決するために、上記の関数は、実際に一致するものが含まれている場合にのみ、get_closest_matchによって返されるリストにインデックスを付けることによって、最も近い一致を返します。difflib.get_close_matches

于 2020-03-28T23:56:11.547 に答える
4

私はFuzzymatcherパッケージを使用しましたが、これはうまくいきました。詳細については、このリンクにアクセスしてください。

以下のコマンドを使用してインストールします

pip install fuzzymatcher

以下はサンプルコードです(上記のRobinLによってすでに提出されています)

from fuzzymatcher import link_table, fuzzy_left_join

# Columns to match on from df_left
left_on = ["fname", "mname", "lname",  "dob"]

# Columns to match on from df_right
right_on = ["name", "middlename", "surname", "date"]

# The link table potentially contains several matches for each record
fuzzymatcher.link_table(df_left, df_right, left_on, right_on)

発生する可能性のあるエラー

  1. ZeroDivisionError:ゼロによる浮動小数点除算--->この リンクを参照して解決してください
  2. OperationalError:そのようなモジュールはありません:fts4->ここからsqlite3.dllをダウンロードし、pythonまたはanacondaDLLフォルダー内のDLLファイルを置き換えます。

長所:

  1. より速く動作します。 私の場合、3000行の1つのデータフレームと、170,000レコードの別のデータフレームを比較しました。これは、テキスト全体でSQLite3検索も使用します。多くよりも速い
  2. 複数の列と2つのデータフレームにわたってチェックできます私の場合、住所と会社名に基づいて最も近いものを探していました。会社名が同じ場合もありますが、住所も確認することをお勧めします。
  3. 同じレコードに最も近いすべての一致のスコアを提供します。カットオフスコアを選択します。

短所:

  1. 元のパッケージのインストールにはバグがあります
  2. 必要なC++とビジュアルスタジオもインストールされています
  3. 64ビットanaconda/Pythonでは動作しません
于 2019-07-12T20:18:11.497 に答える
3

、、およびメソッドfuzzy_pandasを使用できると呼ばれるパッケージがあります。ここにいくつかの素晴らしい例がありますlevenshteinjarometaphonebilenco

import pandas as pd
import fuzzy_pandas as fpd

df1 = pd.DataFrame({'Key':['Apple', 'Banana', 'Orange', 'Strawberry']})
df2 = pd.DataFrame({'Key':['Aple', 'Mango', 'Orag', 'Straw', 'Bannanna', 'Berry']})

results = fpd.fuzzy_merge(df1, df2,
            left_on='Key',
            right_on='Key',
            method='levenshtein',
            threshold=0.6)

results.head()

  Key    Key
0 Apple  Aple
1 Banana Bannanna
2 Orange Orag
于 2020-03-11T10:43:18.020 に答える
2

ヘッドアップとして、これは基本的に機能します。ただし、一致するものが見つからない場合、またはいずれかの列にNaNがある場合を除きます。を直接適用するget_close_matchesよりも、次の関数を適用する方が簡単であることがわかりました。NaN置換の選択は、データセットに大きく依存します。

def fuzzy_match(a, b):
    left = '1' if pd.isnull(a) else a
    right = b.fillna('2')
    out = difflib.get_close_matches(left, right)
    return out[0] if out else np.NaN
于 2014-08-07T18:33:26.430 に答える
2

そのためにd6tjoinを使用できます

import d6tjoin.top1
d6tjoin.top1.MergeTop1(df1.reset_index(),df2.reset_index(),
       fuzzy_left_on=['index'],fuzzy_right_on=['index']).merge()['merged']

index number index_right letter 0 one 1 one a 1 two 2 too b 2 three 3 three c 3 four 4 fours d 4 five 5 five e

次のようなさまざまな追加機能があります。

  • 参加の質を確認し、参加前と参加後
  • 類似度関数をカスタマイズします。たとえば、距離の編集とハミング距離の比較
  • 最大距離を指定します
  • マルチコアコンピューティング

詳細については、を参照してください

于 2018-08-15T13:00:17.390 に答える
1

fuzzywuzzの既存の動作とキーワードを一致させながら、最小限の方法で使用しmergeましたpandas

threshold一致するものを指定するだけです(0との間100):

from fuzzywuzzy import process

def fuzzy_merge(df, df2, on=None, left_on=None, right_on=None, how='inner', threshold=80):
    
    def fuzzy_apply(x, df, column, threshold=threshold):
        if type(x)!=str:
            return None
        
        match, score, *_ = process.extract(x, df[column], limit=1)[0]
            
        if score >= threshold:
            return match

        else:
            return None
    
    if on is not None:
        left_on = on
        right_on = on

    # create temp column as the best fuzzy match (or None!)
    df2['tmp'] = df2[right_on].apply(
        fuzzy_apply, 
        df=df, 
        column=left_on, 
        threshold=threshold
    )

    merged_df = df.merge(df2, how=how, left_on=left_on, right_on='tmp')
    
    del merged_df['tmp']
    
    return merged_df

サンプルデータを使用して試してみてください。

df1 = pd.DataFrame({'Key':['Apple', 'Banana', 'Orange', 'Strawberry']})

df2 = pd.DataFrame({'Key':['Aple', 'Mango', 'Orag', 'Straw', 'Bannanna', 'Berry']})

fuzzy_merge(df, df2, on='Key', threshold=80)
于 2021-03-27T15:48:56.537 に答える
0

行を多くの列と一致させるためのより複雑なユースケースでは、recordlinkagepackageを使用できます。recordlinkageデータフレーム間の行をあいまい一致させるためのすべてのツールを提供しますpandas。これは、マージ時にデータの重複排除に役立ちます。パッケージに関する詳細な記事をここに書きました

于 2020-11-19T06:23:56.653 に答える
0

結合軸が数値の場合、これを使用して、指定された許容誤差を持つインデックスを照合することもできます。

def fuzzy_left_join(df1, df2, tol=None):
    index1 = df1.index.values
    index2 = df2.index.values

    diff = np.abs(index1.reshape((-1, 1)) - index2)
    mask_j = np.argmin(diff, axis=1)  # min. of each column
    mask_i = np.arange(mask_j.shape[0])

    df1_ = df1.iloc[mask_i]
    df2_ = df2.iloc[mask_j]

    if tol is not None:
        mask = np.abs(df2_.index.values - df1_.index.values) <= tol
        df1_ = df1_.loc[mask]
        df2_ = df2_.loc[mask]

    df2_.index = df1_.index

    out = pd.concat([df1_, df2_], axis=1)
    return out
于 2021-02-27T18:11:10.290 に答える