haracha
@haracha

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

python 四分位範囲で外れ値を除外したいんです・・

解決したいこと

ここに解決したい内容を記載してください。

初心者です。

pythonで機械学習のモデルを作成するために使用するデータの整理をしています。
エクセルファイルから取り込んだデータについて、
各項目の数値について、四分位範囲で外れ値を除外したいのですが、うまくいきません。。
取り込んだエクセルファイルにnorm_1という名前をつけ、
各項目の4分位範囲から外れ値を除外したいのです。
第1四分位数(norm_25)、第3四分位数(norm_75)、第3-第1四分位数(norm_iqr)などの抽出はかかるのですが、
いざ、外れ値を除外しようとするとできません。
以前(2年くらい前)は、このコードで除外できていたのですが・・・
色々ネットで調べたりしたのですが分かりません。
どなたか詳しい方いらっしゃいませんでしょうか?

発生している問題・エラー

``
エラーというかこのようなメッセージが表示されます。

/var/folders/z8/c5bhpvld7312cm8j42r7c9qc0000gn/T/ipykernel_40556/1535641706.py:1: FutureWarning: Automatic reindexing on DataFrame vs Series comparisons is deprecated and will raise ValueError in a future version. Do `left, right = left.align(right, axis=1, copy=False)` before e.g. `left == right`
  norm_2 = norm_1[(norm_25 - 1.5 * norm_iqr <= norm_1) & (norm_1 <= norm_75 + 1.5 * norm_iqr)]

または、問題・エラーが起きている画像をここにドラッグアンドドロップ
スクリーンショット 2023-07-06 13.25.45.png
スクリーンショット 2023-07-06 13.18.06.png

0

1Answer

pythonって&ではなくandではないでしょうか?

# 現
norm_2 = norm_1[(norm_25 - 1.5 * norm_iqr <= norm_1) & (norm_1 <= norm_75 + 1.5 * norm_iqr)]

# 修正後
norm_2 = norm_1[(norm_25 - 1.5 * norm_iqr <= norm_1) and (norm_1 <= norm_75 + 1.5 * norm_iqr)]
0Like

Comments

  1. @haracha

    Questioner

    コメントありがとうございます。
    「and」にしてみたら、以下のようなエラーが出ました。。

    スクリーンショット 2023-07-11 13.23.16.png
    スクリーンショット 2023-07-11 13.26.53.png

    2枚目の画像はネットで引っ張ってきたものなのですが、「and」と「&」は用途が違うということですか?

  2. すみません、僕自身勘違いしていたようです。

    普通、Pythonにおいて(多くのプログラミング言語において)&はビット演算を表します。

    print(3&4) #結果は0
    print(3&5) #結果は1
    print(3&6) #結果は2
    

    これは二進数表記してある桁が両方1なら1、片方でも0なら0にします。
    3は二進数で011(2)、4は二進数で100(2)、5は二進数で101(2)、6は二進数で110(2)です。よって3&4は0、3&4は011(2)、3&6は010(2)となります。

    という計算方法が普通なのですが、Pandasのデータフレームなどの条件では&を使うみたいですね。
    見当違いな解答をしてしまいました、すみません。

    ここから本題

    エラーの原因を追加調査してみたのですが、pandasのバージョンによって異なるようで、こちらの環境では上手く再現できませんでした。
    ですので、間違っているかもしれませんが、参考にして下さい。
    まず(norm_25 - 1.5 * norm_iqr <= norm_1) & (norm_1 <= norm_75 + 1.5 * norm_iqr)だけを実行してみてください。
    おそらく以下のような感じになるのではないでしょうか? (※実際のデータを見ていないので、TrueとかFalseは適当ですが……)

      TSH   FT3   FT4  …
    0 True  False True …
    1 True  True  True …
    2 False True False …
    

    つまり、0番目の患者データはFT3が外れ値、他は正常。
    1番目の患者データは全部が正常。
    2番目の患者データはTSHが外れ値、他は正常。

    のように、各項目について外れ値か否かが格納されていませんか?
    もしそうでなければ、別な問題があると思います。

    もしそうなら

    この状況下でnorm_1[略]を実行してしまうと、「一つでもFalseだったらアウトなの? それとも一つでもTrueならOKなの?」というのが分からなくなってしまいます。そこで、.all(1).any(1)のように書く事で、前者なら「一つでもFalseだったらアウト」、後者なら「一つでもTrueならOK」にできます。

    
    is_valid = (norm_25 - 1.5 * norm_iqr <= norm_1) & (norm_1 <= norm_75 + 1.5 * norm_iqr)
    norm_1[is_valid.all(1)]
    
    
    
  3. @haracha

    Questioner

    コメントどうもありがとうございます!
    本当にありがたいです。

    (norm_25 - 1.5 * norm_iqr <= norm_1) & (norm_1 <= norm_75 + 1.5 * norm_iqr)だけを実行してみてみたところ、おっしゃる通り、各項目についてTrueやFalseが表示されました。

    というわけで、is_valid = (norm_25 - 1.5 * norm_iqr <= norm_1) & (norm_1 <= norm_75 + 1.5 * norm_iqr)norm_1[is_valid.all(1)]
    を実行したみたところ、データがゼロになってしまいました。。。

    項目ごとにみてみても、例えば、ALPを見るとかなりFalseがあったり、他の項目においてもこんなに外れ値が存在するのがおかしいです(一応健常人と思われるデータなので)。
    簡単でデータファイルを作成して実行してみます。
    スクリーンショット 2023-07-12 19.27.28.png

    ただ、前から出てくるこのメッセージがよく分かりません。。。
    FutureWarning: Automatic reindexing on DataFrame vs Series comparisons is deprecated and will raise ValueError in a future version. Do left, right = left.align(right, axis=1, copy=False) before e.g. left == right

  4. エラーメッセージの意味は

    TSH 1.5
    T4 0.5
    T3 0.2

    というデータと

    ALB TSH T3 T4
    1 2 3 3
    4 5 6 3

    みたいなテーブルを比較するのは辞めてくださいという意味です。
    実際、最後の画像の Out [156] ではALBの列が映っています。

    よく分からないのが、一番最初の画像の In [97] のブロックにあるnorm_1 = norm_1[cols]でALBの行はカットされているはずですよね? なのに Out [156] でカットされていないのは変です。
    可能性としては、「norm_1の内容を途中で上書きした」「"norm_イチ"と"norm_エル"が混在している」などが考えられます。

    まずはノートブックをコピーし、適宜要らない行を削除しながら、最初から順番に実行するのをオススメします。
    また、その際norm_1 = norm_1[cols]というようにテーブルを上書きするのは避けた方がいいと考えます。今回のような「あれ、今のnorm_1って何が入ってるんだっけ?」と言う事態に陥りかねません。
    不都合がないなら

    data=pd.read_csv("filename.csv")
    cols=[...]
    data_of_interest = data[cols]
    

    のように名前を変えてしまう事をオススメします。

  5. @haracha

    Questioner

    ありがとうございます!
    そういう意味のメッセージなのですね。
    ちなみにALBはカットはしておりませんで、In97の表示は全ての項目が表示しきれていないです。

    @TBjusticeさんにご指摘いただいたように、テーブルを上書きしないようにして、また、簡単なエクセルファイル(test2という名称)を作成して実行してみたところうまくいきました。

    スクリーンショット 2023-07-14 17.37.06.png
    スクリーンショット 2023-07-14 17.49.43.png
    norm_1の方は私がへんなことをしていると思われるの一つずつ確認してみます!

    もう一つお聞きしたいのですが、欠損値がある場合についてです。今現在、欠損値があると除外されてしまう状態になっているのですが(上のtest.xlsxは欠損値なしです)、欠損値があるファイル(test2.xlsx:testのファイルから一部データを消しました)においては欠損値の部分は「false」と判断されて外れ値扱い?になり除外されてしまっています。欠損値がある項目について、値がある部分だけみて外れ値を除外するということはできるんでしょうか?
    norm_1のファイルは欠損値だらけなんです。
    スクリーンショット 2023-07-14 18.00.53.png
    スクリーンショット 2023-07-14 18.01.36.png

    testの方では5番のデータだけが残るんですが、test2で5番に欠損値があるとそれも除外されてしまいました。。
    スクリーンショット 2023-07-14 18.12.55.png

    testとtest2の内容はこのようになっています。

  6. あまり綺麗とは言えない書き方ですが、参考になれば幸いです。

    import pandas as pd
    
    data=pd.read_csv("data.csv")
    

    dataframe.head()は最初の5行を表示します。

    data.head()
    
    TSH T3 DoNotUse T4
    0 5 15.0 5 25
    1 6 16.0 5 26
    2 6 17.0 5 2
    3 7 1.0 5 24
    4 7 NaN 5 24
    cols=["TSH", "T3", "T4"]
    

    DoNotUse行を省く

    data_of_interest = data[cols]
    data_of_interest.head()
    
    TSH T3 T4
    0 5 15.0 25
    1 6 16.0 26
    2 6 17.0 2
    3 7 1.0 24
    4 7 NaN 24
    norm_25 = data_of_interest.quantile(0.25)
    norm_75 = data_of_interest.quantile(0.75)
    norm_iqr = norm_75 - norm_25
    
    norm_iqr
    
    TSH    2.0
    T3     1.0
    T4     2.0
    dtype: float64
    

    外れ値にFalseが入ったテーブルを作成。この段階ではNanも外れ値扱い。

    is_valid = (norm_25 - 1.5 * norm_iqr <= data_of_interest) & (data_of_interest <= norm_75 + 1.5 * norm_iqr)
    is_valid.head()
    
    TSH T3 T4
    0 True True True
    1 True True True
    2 True True False
    3 True False True
    4 True False True

    dataframe.isnull()でnanだけをTrueにしたテーブルを作る。

    is_nan=data_of_interest.isnull()
    is_nan.head()
    
    TSH T3 T4
    0 False False False
    1 False False False
    2 False False False
    3 False False False
    4 False True False

    is_valid |= is_nanis_valid = is_valid | is_nan)でis_valid内でNanとしてはぶかれていたものを、無理やりTrueにする。

    is_valid |= is_nan
    is_valid.head()
    
    TSH T3 T4
    0 True True True
    1 True True True
    2 True True False
    3 True False True
    4 True True True
    valid_data = data_of_interest[is_valid.all(axis=1)]
    valid_data.head()
    
    TSH T3 T4
    0 5 15.0 25
    1 6 16.0 26
    4 7 NaN 24
    5 5 15.0 25
    6 6 16.0 26
  7. @haracha

    Questioner

    ありがとうございます!
    この方法で書いてみます!

  8. @haracha

    Questioner

    @TBjustice
    教えていただいた通りに実行してみたらできました!
    ありがとうございました。あとは、本当に自分が使用したいファイルでできるようにですね。
    本当に助かりました!

Your answer might help someone💌