質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

1回答

1253閲覧

[python]データフレームの同一カラム内の全データから成る分布から外れるデータをNaNに置換した形で返すコードをスマートに書けずに悩んでいます

mini1988

総合スコア56

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

0クリップ

投稿2020/10/16 12:49

編集2020/10/17 10:37

困っていること

データフレームの各列に対して分布から外れるデータを除去した形で返したくて、自作で関数を作りました。所望の動作はするのですが、冗長な感じが抜けず、もっとスマートなコードにしたいと考えています。おそらく、lambda, map(), apply(), applymap()などを使って、やることになるのかなとも思うのですが、なかなか書けません。どなたか、もっとシンプルにスマートなコードを書く方法を教えていただけないでしょうか?お手数ですが、よろしくお願いします。

想定していること

私がやりたいのは、「同一のカラムの中にある全データに対して分布を取り(行方向ではない)、その分布から外れるデータの箇所をNANに置換して返す」というものです。以下の場合だと、カラムAのインデックス1に数値1000が入っていますが、数値1000はカラムA全体のデータ分布からある一定の条件を持って外れているのでその部分をNANにする、という処理を行いたいです。他のカラム(B列,C列,,,)も同様の処理を行いたい。

### 動作を再現できるデータフレーム疑似生成 ### import numpy as np import pandas as pd import random mu, sigma = 0, (10-i)+1 data = np.array([[random.gauss(mu, sigma) for _ in range(500)],[random.gauss(mu, sigma) for _ in range(500)]]) df=pd.DataFrame(data.T,columns=["A","B"]) df.loc[1,"A"]=1000 df.loc[3,"B"]=-1000 A B 0 -5.215559 15.548085 1 1000.000000 7.173219 2 -8.806023 0.233582 3 -17.739273 -1000.000000 4 3.270542 6.586852 ... ... ... 495 18.536944 23.445213 496 0.197173 8.680920 497 -8.324104 -4.110047 498 1.917041 -8.685127 499 -8.054887 -11.455291

↓↓↓↓↓↓ 変換後に期待している出力

A B 0 -5.215559 15.548085 1 NAN 7.173219 2 -8.806023 0.233582 3 -17.739273 NAN 4 3.270542 6.586852 ... ... ... 495 18.536944 23.445213 496 0.197173 8.680920 497 -8.324104 -4.110047 498 1.917041 -8.685127 499 -8.054887 -11.455291

私が書いた自作コード

def remove_outlier(dataframe): results_all=[] for c in dataframe.columns: q1 = dataframe[c].quantile(0.3) q3 = dataframe[c].quantile(0.7) iqr = q3 - q1 fence_low = q1-2*iqr fence_high = q3+2*iqr result=[] for i,k in zip(range(len(dataframe)),[(dataframe[c] > fence_low) & (dataframe[c] < fence_high)][0].values): if k==False: result.append(np.nan) else: result.append(dataframe[c][i]) results_all.append(result) new_df=pd.DataFrame(np.array(results_all).T) new_df.columns = dataframe.columns return new_df

再度:追記

import numpy as np import pandas as pd import random ### 想定しているデータフレーム def dataset(): mu, sigma = 0, (10-i)+1 data = np.array([[random.gauss(mu, sigma) for _ in range(500)],[random.gauss(mu, sigma) for _ in range(500)]]) dataframe=pd.DataFrame(data.T,columns=["A","B"]) dataframe.loc[1,"A"]=1000 dataframe.loc[3,"B"]=-1000 return dataframe df=dataset() print(df.head(5)) A B 0 -16.397471 -15.766524 1 1000.000000 -2.913148 2 20.302729 -6.395709 3 -6.314884 -1000.000000 4 -5.692132 -2.014456 ### 外れている値をNANへ変換する自作関数 def remove_outlier(dataframe): results_all=[] for c in dataframe.columns: q1 = dataframe[c].quantile(0.3) q3 = dataframe[c].quantile(0.7) iqr = q3 - q1 fence_low = q1-2*iqr fence_high = q3+2*iqr result=[] for i,k in zip(range(len(dataframe)),[(dataframe[c] > fence_low) & (dataframe[c] < fence_high)][0].values): if k==False: result.append(np.nan) else: result.append(dataframe[c][i]) results_all.append(result) new_df=pd.DataFrame(np.array(results_all).T) new_df.columns = dataframe.columns return new_df ### 自作関数処理した結果を表示したもの(期待している出力) print(remove_outlier(df).head(5)) A B 0 -16.397471 -15.766524 1 NaN -2.913148 2 20.302729 -6.395709 3 -6.314884 NaN 4 -5.692132 -2.014456

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

meg_

2020/10/16 12:58

速度が遅すぎるのでなければ、自分が理解できるコードの方が良いかと思いますよ。
toast-uz

2020/10/16 14:00 編集

このコードの目的・仕様は何でしょうか? 「分布から外れるデータを除去」という意図とのことですが、そのように動いていないように思います。 (何度か乱数でデータフレーム作って入れて動きを見てみたら、稀に分布から外れの途中だけNanになって、一番外れている値はそのままとかになりました) 前提としているデータフレームの条件(形)がよくわかりません。 1行のみのデータフレームを与えたら、全てNanになりました。
mini1988

2020/10/17 05:22

ご指摘いただき、ありがとうございます。説明不足で申し訳ありませんでした。質問内容に関して追記いたしました。どうぞよろしくお願いします。
toast-uz

2020/10/17 06:38

ありがとうございます。コードを検討するために省略されていないテスト用DataFrameを作りたく、 df = pd.DataFrame([ [0, 0], [0, -100], [0, 0], [100, 0], [0, 0], [0, 0], [0, 100], [0, 0], [-100, 0], [0, 0]], columns=['A', 'B']) と作ってみたのですが、remove_outlier(df)が全てNaNになってしまい困っています。なぜでしょう。
mini1988

2020/10/17 08:25

説明不足で申し訳ありません。私が想定しているデータではないものを質問欄に提示してしまってました。私が想定しているデータは、列ごとのデータ全体でみると正規分布などをしています。たとえばカラムAに入っているデータだけ見ると、基本的には正規分布しているのですが、その分布から大きく外れている値も入っています。その大きく外れている値をNANに置換して返してくれるコードを書こうとしていて、自作もしたつもりです。ただあまりにもコードが冗長なので、簡単にしたいと思っているのですが、どうすれば実現できるのか、どなたかわかる方に助けて欲しいというお願いでした。もし可能でしたらよろしくお願いします。
toast-uz

2020/10/17 08:38

簡単にしたコードを作りたいのですが、ロジックだけ見て期待通り動くコードを作るのは難関なので、動作を再現出来るデータをもとに、質問者様のコードと実行結果を見比べながら考えたいのです。動作可能なデータの例を提示頂くことは難しいのでしょうか?
mini1988

2020/10/17 10:12 編集

説明不足で申し訳ありません。質問欄において、動作を再現できるデータフレームを”想定していること”に記載してみたのですが、いかがでしょうか?
toast-uz

2020/10/17 10:20

途中が省略されているので、これをもとに動作が再現できないのですよね。悩ましいです。
mini1988

2020/10/17 10:35 編集

説明不足で申し訳ないです。質問欄の末尾に「再度:追記」を書いてみました。質問がへたくそでごめんなさい。
guest

回答1

0

ベストアンサー

Series.whereで条件に一致しない値を置き換えることができます。
また、DataFrame.applyで各列に同じ関数を適用することができます。

python

1def remove_outlier(series): 2 q1 = series.quantile(0.3) 3 q3 = series.quantile(0.7) 4 iqr = q3 - q1 5 fence_low = q1 - 2 * iqr 6 fence_high = q3 + 2 * iqr 7 8 return series.where((series > fence_low) & (series < fence_high)) 9 10new_df = df.apply(remove_outlier) 11

投稿2020/10/17 10:40

copepoda

総合スコア324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mini1988

2020/10/19 01:41

素敵なコードを提供していただき、ありがとうございます。大変助かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問