1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【python】pandasで異なるdf同士の判定をする

Last updated at Posted at 2022-01-26

やりたいこと

df_A

kome gu date
ササニシキ うめ 20210211
ササニシキ たまご 20210213
コシヒカリ おかか 20210315
コシヒカリ しゃけ 20210511

df_B

gu santi
うめ 和歌山
しゃけ 北海道
おかか 鹿児島
こんぶ 北海道
いくら 北海道

df_Aとdf_Bを使って、df_Aをこうしたい

kome gu date flag
ササニシキ うめ 20210211 和歌山
ササニシキ たまご 20210213
コシヒカリ おかか 20210315 鹿児島
コシヒカリ しゃけ 20210511 北海道

guが一致したらdf_Bの値をdf_Aに追加するイメージ。
前提としてdf_Aとdf_Bのguは一意なものとします。

参考文献はこちら → https://note.nkmk.me/python-pandas-where-mask/

準備

サンプルを作成します。
おにぎりなのは趣味です。

import pandas as pd
import numpy as np
import time

df_A = pd.DataFrame({'kome': ["ササニシキ", "ササニシキ","コシヒカリ","コシヒカリ"],
                   'gu': ["うめ","たまご","おかか", "しゃけ"],
                   'date': [20210211, 20210213, 20210315, 20210511]})

df_B = pd.DataFrame({'gu': ["うめ", "しゃけ", "おかか","こんぶ","いくら"],
                   'santi': ["和歌山", "北海道", "鹿児島", "北海道", "北海道"]})

トライ1

whereを使う方法。
あらかじめ空の列を作成する必要があります。

df_A["flag"] = ""
_start = time.time()

for i in range(len(df_B)):
    df_A["flag"].where(df_A["gu"]==df_B.at[i, "gu"],df_B.at[i, "santi"])

# 計測
print(time.time() - _start)
print(df_A)


# 出力結果
# >>> print(time.time() - _start)
# 0.14820408821105957
# >>> print(df_A)
#     kome   gu      date flag
# 0  ササニシキ   うめ  20210211
# 1  ササニシキ  たまご  20210213
# 2  コシヒカリ  おかか  20210315
# 3  コシヒカリ  しゃけ  20210511

なぜかうまくいかない。
上書きしないようにしていたのでうまくいかないのは当たり前でした。

が、inplace=Trueにしてもなんか変。

df_A["flag"] = ""
_start = time.time()

for i in range(len(df_B)):
    df_A["flag"].where(df_A["gu"]==df_B.at[i, "gu"],df_B.at[i, "santi"] , inplace=True)

# 計測
print(time.time() - _start)
print(df_A)


# 出力結果
# >>> print(time.time() - _start)
# 0.1444559097290039
# >>> print(df_A)
#     kome   gu      date flag
# 0  ササニシキ   うめ  20210211  北海道
# 1  ササニシキ  たまご  20210213  北海道
# 2  コシヒカリ  おかか  20210315  北海道
# 3  コシヒカリ  しゃけ  20210511  北海道

潔くあきらめる。
人生諦めが肝心。

トライ2

locを使う方法。
存在しない列を設定してもOK。

_start = time.time()

for i in range(len(df_B)):
    df_A.loc[df_A["gu"] == df_B.at[i, "gu"],'flag'] = df_B.at[i, "santi"]    

# 計測
print(time.time() - _start)
print(df_A)

# 出力結果
# >>> print(time.time() - _start)
# 0.13238096237182617
# >>> print(df_A)
#     kome   gu      date flag
# 0  ササニシキ   うめ  20210211  和歌山
# 1  ササニシキ  たまご  20210213  
# 2  コシヒカリ  おかか  20210315  鹿児島
# 3  コシヒカリ  しゃけ  20210511  北海道

うまくいった。
あと微妙にwhereより早めな希ガス。

というわけで採用じゃー!

トライ3(1/27追記)

コメントいただき、mergeをやってみようと思いました。
実環境のデータは列名不一致&df_Bが100列相当なので見直し版。
※これはデータが一意な場合の話になります

import pandas as pd
import numpy as np
import time

df_A = pd.DataFrame({'kome': ["ササニシキ", "ササニシキ","コシヒカリ","コシヒカリ"],
                   'gu': ["うめ","たまご","おかか", "しゃけ"],
                   'date': [20210211, 20210213, 20210315, 20210511]})

# 列名の変更とダミー列の追加
df_B = pd.DataFrame({'sozai': ["うめ", "しゃけ", "おかか","こんぶ","いくら"],
                   'santi': ["和歌山", "北海道", "鹿児島", "北海道", "北海道"],
                   'santi2': ["和歌山", "北海道", "鹿児島", "北海道", "北海道"]})

#処理開始
_start = time.time()
df_A = pd.merge(df_A, df_B[["sozai", "santi"]], how="left", left_on="gu", right_on="sozai")
df_A = df_A.drop("sozai",axis=1)

# 計測
print(time.time() - _start)
print(df_A)


# 出力結果
# >>> print(time.time() - _start)
# 0.12956595420837402
# >>> print(df_A)
#     kome   gu      date santi
# 0  ササニシキ   うめ  20210211   和歌山
# 1  ササニシキ  たまご  20210213   NaN
# 2  コシヒカリ  おかか  20210315   鹿児島
# 3  コシヒカリ  しゃけ  20210511   北海道

santiに該当する部分は名前共通なのでリネームしなくてもいいかなという。
スマートでよき!

1/27追記そのに

なんかおかしいなと思ったら、実環境のデータはキーが一意じゃなかったです。
ので、当方は2の方法を使います……。
確認不足ここに極まれり。

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?