pandas.DataFrameで置換処理

概要

  • DataFrameでの置換処理をforループでやっているが時間がかかりすぎる
  • where()を用いて一括で処理できる

where()の引数は「残す条件」

先に一番間違えやすい点を書いておきます。where() の第1引数は置換する条件ではなく、元の値を残す条件です。

df.where(残す条件, 置換に使う値)

条件がTrueの場所は元の値がそのまま残り、Falseの場所が置換されます。名前から受ける印象と逆なので、ここを取り違えると結果が反転します。私も最初は毎回どちらか分からなくなっていました。

逆の動きがほしい場合は mask() を使います。こちらは条件がTrueの場所を置換します。

df.where(df >= 0, 0)  # 0未満を0にする(0以上を残す)
df.mask(df < 0, 0)    # 0未満を0にする(0未満を置換する)

上の2行はまったく同じ結果になります。読みやすい方を選べば大丈夫です。

解決方法

準備

import numpy as np
import pandas as pd

cols = ['var1', 'var2', 'var3', 'var4']
df1 = pd.DataFrame(np.random.randn(4, 4), columns=cols)
df2 = pd.DataFrame(np.arange(16).reshape(4, 4), columns=cols)

df1

       var1      var2      var3      var4
0 -0.083782  0.964222  0.832664 -0.528963
1  0.017696  0.144067  0.093823  0.147779
2 -0.082808 -0.893112 -0.477983 -0.623641
3  0.581019 -1.603081 -0.717007  0.849844

df2

   var1  var2  var3  var4
0     0     1     2     3
1     4     5     6     7
2     8     9    10    11

例:df1の要素のうち、負の要素をdf2の値で置換する

"""置換対象.where(残す条件, 代入する対象)"""
df1.where(df1 >= 0, df2)

       var1       var2       var3       var4
0  0.000000   0.964222   0.832664   3.000000
1  0.017696   0.144067   0.093823   0.147779
2  8.000000   9.000000  10.000000  11.000000
3  0.581019  13.000000  14.000000   0.849844

例:特定の列だけに対して置換処理を行う

下記の場合、var2列がゼロ以上の場合はvar1列の値を用い、負の場合はvar3列の値を用いた列を作成できる。

df1['var1'].where(df1['var2'] >= 0, df1['var3'])

0   -0.083782
1    0.017696
2   -0.477983
3   -0.717007
Name: var1, dtype: float64

つまずきやすい点

元のDataFrameは変更されない

where() も結果を新しいDataFrameとして返すので、元のDataFrameはそのままです。受け取らないと変更が反映されていないように見えます。

df1 = df1.where(df1 >= 0, df2)

行と列のラベルで対応づけられる

第2引数にDataFrameやSeriesを渡した場合、位置ではなく行名・列名で対応づけて値が取られます。ラベルが一致しない部分は NaN になります。

インデックスがずれている2つのDataFrameを組み合わせようとして、結果に NaN が並ぶ場合はここを疑ってください。位置で対応づけたい場合は、事前に reset_index(drop=True) でインデックスを揃えておくと確実です。

条件を複数指定する場合

条件を組み合わせるときは、and / or ではなく & / | を使い、各条件を括弧でくくります。

df1.where((df1 >= 0) &amp; (df1 &lt; 1), df2)

ここを and で書くと ValueError: The truth value of a Series is ambiguous. になります。理由はValueError: The truth value of a Series is ambiguous.に書きました。

行や列そのものを削除したい場合はpandas.DataFrameで複数行・列を一括で削除するを、Excelファイルの読み込みはpandasを使ってExcelを読み込むを参照してください。