KeyErrorを与えるlen(string)を含む条件式に基づいて、pandas DataFrameから行を削除します


303

私はpandas DataFrameを持っていますが、特定の列の文字列の長さが2より大きい行を削除します。

私はこれを行うことができると期待しています(この回答に従って):

df[(len(df['column name']) < 2)]

しかし、私はただエラーを受け取ります:

KeyError: u'no item named False'

何が悪いのですか?

(注:をdf.dropna()含むNaN行を削除するために使用できることは知っていますが、条件式に基づいて行を削除する方法はわかりませんでした。)

回答:


168

するとlen(df['column name'])、1つの数値、つまりDataFrameの行数(つまり、列自体の長さ)が取得されます。len列の各要素に適用する場合は、を使用しますdf['column name'].map(len)。だから試して

df[df['column name'].map(len) < 2]

3
リスト内包表記を使用する方法を思いつきましたdf[[(len(x) < 2) for x in df['column name']]]が、あなたの方がはるかに優れています。ご協力いただきありがとうございます!
sjs

13
誰かがより複雑な比較を必要とする場合、ラムダを常に使用できます。 df[df['column name'].map(lambda x: str(x)!=".")]
4lberto

1
何らかの理由で、@ 4lbertoによって投稿されたものを除いて、他のオプションはどれも私にとってうまくいきませんでした。私はpandas 0.23.4python 3.6を使用しています
goelakash

1
私は追加することになり.copy()引き上げる新しい列を割り当て、例えば(後で編集をしたい場合には、最後にこのデータフレームを警告「A値をデータフレームからのスライスのコピーに設定されしようとしている」。
PlasmaBinturong

807

この質問の元のタイトル「条件式に基づいてpandas DataFrameから行を削除する方法」に直接答えるには(これは必ずしもOPの問題ではないが、この質問に遭遇した他のユーザーを助けることができる)、これを行う1つの方法は、ドロップ方法:

df = df.drop(some labels)

df = df.drop(df[<some boolean condition>].index)

列 'score'が<50であるすべての行を削除するには:

df = df.drop(df[df.score < 50].index)

インプレースバージョン(コメントで指摘されているとおり)

df.drop(df[df.score < 50].index, inplace=True)

複数の条件

ブール索引付けを参照)

演算子は、for 、|for or&for です。これらは括弧を使用してグループ化する必要があります。and~not

列 'score'が<50および> 20であるすべての行を削除するには

df = df.drop(df[(df.score < 50) & (df.score > 20)].index)


32
ドロップ機能がインプレース置換をサポートしていることに注意したいだけです。つまり、あなたのソリューションはdf.drop(df [df.score <50] .index、inplace = True)と同じです。それにもかかわらず、「インデックス」のトリックを知りませんでした。たくさん助けてくれました
Quickbeam2k1

9
このインデックストリックを使用する前に、インデックス値が一意であることを確認する必要があることを指摘してください(またはを呼び出しますreset_index())。多くの行への道が私のデータフレームから削除されたとき、私はこれを難しい方法で見つけました。
ジェイ

3
列の型がstrであるすべての行を削除するにはどうすればよいですか?リストの列タイプのみを保持したい。私が試してみました test = df.drop(df[df['col1'].dtype == str].index)が、私はエラーを取得KeyError: False 私も試してみましたdf.drop(df[df.col1.dtype == str].index)し、df.drop(df[type(df.cleaned_norm_email) == str].index)しかし何も動いていないようにみえましたか?誰もがアドバイスできますか?ありがとう!@ユーザー
PyRsquared

1
これは古い質問ですが... @ aquatically-challenged-fishはこれよりはるかに高速です。df[(df.score < 50) & (df.score > 20)]答えの一部として計算することに注意してください。これを逆にdf = df[(df.score >= 50) | (df.score <= 20)]すると、答えがはるかに速くなります。
Roobie Nuby

1
@RoobieNuby-それらは同じ状態ではありません。
Nguai al

106

DataFrameフィルタリングされたバージョン自体に割り当てることができます。

df = df[df.score > 50]

これは以下より高速ですdrop

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test[test.x < 0]
# 54.5 ms ± 2.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test.drop(test[test.x > 0].index, inplace=True)
# 201 ms ± 17.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test.drop(test[test.x > 0].index)
# 194 ms ± 7.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

or条件を使用して複数の列を確認するにはどうすればよいですか?
Piyush S. Wanare


9

@Userの汎用ソリューションを拡張して、drop無料の代替案を提供します。これは、質問のタイトル(OPの問題ではない)に基づいてここで指示された人々向けです

負の値を持つすべての行を削除したいとします。1つのライナーソリューションは次のとおりです。

df = df[(df > 0).all(axis=1)]

ステップバイステップの説明:-

5x5のランダム正規分布データフレームを生成しましょう

np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,5), columns=list('ABCDE'))
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
1 -0.977278  0.950088 -0.151357 -0.103219  0.410599
2  0.144044  1.454274  0.761038  0.121675  0.443863
3  0.333674  1.494079 -0.205158  0.313068 -0.854096
4 -2.552990  0.653619  0.864436 -0.742165  2.269755

条件がネガを削除しているとします。条件を満たすブールdf:-

df > 0
      A     B      C      D      E
0   True  True   True   True   True
1  False  True  False  False   True
2   True  True   True   True   True
3   True  True  False   True  False
4  False  True   True  False   True

条件を満たすすべての行のブールシリーズ。行の いずれかの要素が条件に失敗した場合、その行はfalseとマークされます。

(df > 0).all(axis=1)
0     True
1    False
2     True
3    False
4    False
dtype: bool

最後に、条件に基づいてデータフレームから行を除外します

df[(df > 0).all(axis=1)]
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
2  0.144044  1.454274  0.761038  0.121675  0.443863

あなたが実際に戻ってDFに割り当てることができ、削除フィルター上で行わingが
df = df[(df > 0).all(axis=1)]

これは、NaN(非数値エントリ)を含む行を除外するように簡単に拡張できます。
df = df[(~df.isnull()).all(axis=1)]

これは、次のような場合にも簡略化できます。列Eが負であるすべての行を削除します

df = df[(df.E>0)]

@Userのdropソリューションが未加工のカラムベースのろ過よりも遅い理由に関するいくつかのプロファイリング統計で終わりたいと思います。

%timeit df_new = df[(df.E>0)]
345 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit dft.drop(dft[dft.E < 0].index, inplace=True)
890 µs ± 94.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

カラムは、基本的にSeries、すなわちA NumPy配列は、任意のコストをかけずにインデックスを付けることができます。基礎となるメモリ組織が実行速度にどのように影響するかに興味がある人のために、ここでパンダの高速化に関する素晴らしいリンクがあります:


6

パンダstr.lenでは、境界を使用して、ブール結果を使用してフィルタリングできます。

df[df['column name'].str.len().lt(2)]

3

列値の複雑な条件に基づいてデータフレームの行を削除する場合は、上記の方法でそれを書き込むことは複雑になる可能性があります。私は常に機能する次のより簡単な解決策を持っています。「ヘッダー」を使用して列をドロップすると想定し、その列をリストに最初に取得します。

text_data = df['name'].tolist()

リストのすべての要素に関数を適用し、それをパンダシリーズに配置します。

text_length = pd.Series([func(t) for t in text_data])

私の場合、私はトークンの数を取得しようとしていました:

text_length = pd.Series([len(t.split()) for t in text_data])

次に、データフレームに上記のシリーズを含む列を1つ追加します。

df = df.assign(text_length = text_length .values)

これで、次のような新しい列に条件を適用できます。

df = df[df.text_length  >  10]
def pass_filter(df, label, length, pass_type):

    text_data = df[label].tolist()

    text_length = pd.Series([len(t.split()) for t in text_data])

    df = df.assign(text_length = text_length .values)

    if pass_type == 'high':
        df = df[df.text_length  >  length]

    if pass_type == 'low':
        df = df[df.text_length  <  length]

    df = df.drop(columns=['text_length'])

    return df
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.