[python] Dataframeから複数条件で行を抽出する


やりたいこと

  • dataframeから複数条件で行を抽出したい
  • データには欠損値が含まれる

試してみたこと

データ抽出にはいくつか方法があるようだけれど今回は下記の2つで試した
1. pandasquery関数を使用する
2. queryを使わないで列名を使用する

サンプルデータ

ID NAME ADDRESS_1 ADDRESS_2 CORPORATIONNUMBER COUNTRY
12345 まるばつ産業 東京都中央区 東京都千代田区 25671900 JAPAN
26890 どれみ物産 NaN 東京都港区 10008765 JAPAN
66635 abc corporation NaN NaN 57682399 USA

抽出条件

  1. COUNTRYがJAPANであること
  2. ADDRESS_1があり、かつADDRESS_2もあること

抽出したい行

ID NAME ADDRESS_1 ADDRESS_2 CORPORATIONNUMBER COUNTRY
12345 まるばつ産業 東京都中央区 東京都千代田区 25671900 JAPAN

出力したいカラム

COUNTRY以外を出力する

ID NAME ADDRESS_1 ADDRESS_2 CORPORATIONNUMBER
12345 まるばつ産業 東京都中央区 東京都千代田区 25671900

条件式をつくる

  1. COUNTRYがJAPANであること
query
COUNTRY=='JAPAN'
queryを使わない
df[COUNTRY]=='JAPAN'
  1. ADDRESS_1があり、かつADDRESS_2もあること = ADDRESS_1が欠損値(NaN)ではなく、ADDRESS_2も欠損値でない
query
# and でつなぐ
ADDRESS_1.notna() and ADDRESS_2.notna()

# & でもよい
ADDRESS_1.notna() & ADDRESS_2.notna()
queryを使わない
# & を使う
df[ADDRESS_1].notna() & df[ADDRESS_2].notna()

ちなみに「または」の条件の時はこうなる

query
# or でつなぐ
ADDRESS_1.notna() or ADDRESS_2.notna()

# | でもよい
ADDRESS_1.notna() | ADDRESS_2.notna()
queryを使わない
# | を使う
df[ADDRESS_1].notna() | df[ADDRESS_2].notna()
  • queryではandorが使えるが、queryを使わない場合は使えないので注意

条件式を組み立てる

query
# 条件を&でつないでダブルクォーテーションでくるむ
df.query("COUNTRY =='JAPAN' & ADDRESS_1.notna() & ADDRESS_2.notna()")
queryを使わない
# 条件を&でつないでダブルクォーテーションでくるむ
df.query("df[COUNTRY] =='JAPAN' & df[ADDRESS_1].notna() & df[ADDRESS_2].notna()")

抽出したい列を選択する

  • locを使う
  • loc[対象行:,(列名)]
列選択
# 全ての行を対象として'ID','NAME','ADDRESS_1','ADDRESS_2','CORPORATIONNUMBER'列を抽出する場合
loc[:,('ID','NAME','ADDRESS_1','ADDRESS_2','CORPORATIONNUMBER')]

# IDが12345の行を対象として'ID','NAME','ADDRESS_1','ADDRESS_2','CORPORATIONNUMBER'列を抽出する場合
loc['12345' :,('ID','NAME','ADDRESS_1','ADDRESS_2','CORPORATIONNUMBER')]

サンプルコード

test
import pandas as pd
import codecs

# 読み込むcsv
target = 'sample.csv'
# 出力先
out = 'output.tsv'

with codecs.open(target, mode='r', encoding='shift-jis', errors='ignore') as f:
    df = pd.read_csv(
            f,
            sep=',',
            engine='python',    # encodingのかわりにengine='python'にするとおまかせになるらしい
            # 特定のカラムだけ出力したい時は```usecols```で設定する
            usecols=[
                'ID',
                'NAME',
                'ADDRESS_1',
                'ADDRESS_2',
                'CORPORATIONNUMBER',
                'COUNTRY'
            ],
            # 一見数値に見えるカラムの値も文字として扱いたいので文字列にコンバートしておく
            # 何もしないと出力時に小数点以下がついたりするので注意
            converters={
                'ID': str,
                'CORPORATIONNUMBER': str,
                }
    )

    # データ抽出, 列選択
    result = df.query("COUNTRY =='JAPAN' & ADDRESS_1.notna() & ADDRESS_2.notna()").loc[:,('ID','NAME','ADDRESS_1','ADDRESS_2','CORPORATIONNUMBER')]

    # または
  result = df.query("df[COUNTRY] =='JAPAN' & df[ADDRESS_1].notna() & df[ADDRESS_2].notna()").loc[:,('ID','NAME','ADDRESS_1','ADDRESS_2','CORPORATIONNUMBER')]

    # 出力
    result.to_csv(out, sep='\t',encoding='utf-8',index=False)