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

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

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

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

pandas

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

データマイニング

データマイニングは、購買履歴やクレジットカードの利用履歴、電話の通話履歴など企業にある大量のデータを解析して、その中に隠れたパターンやルールを探し出す技術です。DMと略されることもあります。

Q&A

解決済

3回答

3121閲覧

PandasのDataFrameでの条件付き内挿

rneko

総合スコア18

Python

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

pandas

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

データマイニング

データマイニングは、購買履歴やクレジットカードの利用履歴、電話の通話履歴など企業にある大量のデータを解析して、その中に隠れたパターンやルールを探し出す技術です。DMと略されることもあります。

0グッド

2クリップ

投稿2019/08/24 23:22

前提・実現したいこと

以下のようなデータフレームの,欠測値の内挿を行いたいです.
そして更に,この時系列データを内挿するのに,**「欠測が1つだけ存在する場合のみ線形内挿する」**という条件を付与したいのです.

python

1df = pd.DataFrame({"田中_身長":[129, pd.np.nan, 120, 133.4, 132, 150.5, 200.5], 2 "田中_体重":[29, 29.5, pd.np.nan, pd.np.nan, pd.np.nan, 50.5, 200], 3 "山田_身長":[180.6, 180.5, pd.np.nan, 177.3, 169, 170.2, 160.5], 4 "山田_体重":[80.6, 80.5, 81, pd.np.nan, 69, 70.2, 60.5]}, 5 index=pd.date_range('2016/01/01 00:00', freq='1Y', periods=7))

具体例

上により作成される時系列データは

-田中_身長田中_体重山田_身長山田_体重
2016-12-3112929180.680.6
2017-12-31NaN29.5180.580.5
2018-12-31120NaNNaN81
2019-12-31133.4NaN177.3NaN
2020-12-31132NaN16969
2021-12-31150.550.5170.270.2
2022-12-31200.5200160.560.5

となりますが,これを条件通りに内挿し,以下のようなデータフレームを作成します.

-田中_身長田中_体重山田_身長山田_体重
2016-12-3112929180.680.6
2017-12-31124.529.5180.580.5
2018-12-31120NaN178.981
2019-12-31133.4NaN177.375
2020-12-31132NaN16969
2021-12-31150.550.5170.270.2
2022-12-31200.5200160.560.5

このように,2つ以上欠測が連続した場合,内挿をしないようにします.

発生している問題

内挿するために以下の処理を行いました.

python

1df.interpolate(limit=1, method="time", limit_area='inside', limit_direction="backward")

しかし,このような書き方をしても,以下のような結果となり,うまくいきません(山田の2020年の体重が線形内挿されてしまっています).

-田中_身長田中_体重山田_身長山田_体重
2016-12-3112929180.680.6
2017-12-31124.529.5180.580.5
2018-12-31120NaN178.981
2019-12-31133.4NaN177.375
2020-12-3113245.2516969
2021-12-31150.550.5170.270.2
2022-12-31200.5200160.560.5

limitの意味合い的に正しい挙動なのは分かりますが,正しい答えを導く方法が分かりません.
rollingやresampleなども使って見ようと思いましたが,良いやり方が思いつきません.

どのようにするのが良いのでしょうか.ループを書いて一行一行処理するのは,Pythonっぽくない書き方で避けたいと思います.

よろしくお願いいたします.

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

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

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

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

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

meg_

2019/08/25 01:25

ループ処理しかないように思われます。
rneko

2019/08/25 02:33

meg_さん ご指摘いただきありがとうございます. どこまでテクニックを使って,どこまで力任せに解いてというのは毎回悩みます.参考になりました.ありがとうございます.
meg_

2019/08/25 02:38

pandasのループ処理は遅いと言われますが、必要なときは使います。可読性も大事だと思っています。
rneko

2019/08/25 02:59

meg_さん 確かに可読性などを考えると,ループも捨てがたいですね.何より,ループを書くまいと悩んでいる時間が長くて,結局答えを得られないこともありますから,柔軟に考えていきたいとおもいます.
guest

回答3

0

python

1#nanがある行取得 2null_place = df.loc[:, df.isnull().any()]  3 4#2回目以上なのか判定 5if any(null_place.groupby(list(null_place.columns)).cumcount() >= 1):: 6 pass 7else: 8 欠測が1つだけ存在する場合の処理

だと思います

投稿2019/08/25 02:15

編集2019/08/25 02:18
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

rneko

2019/08/25 02:32

susuuaaさん ご回答ありがとうございます. groupbyをこういうときにも使うことができるのですね.ヒストグラムを書くときくらいにしか使わなかった気がしますが,実際のこのような計算の過程で使えるのは面白いです. 勉強になりました.ありがとうございます.
guest

0

ベストアンサー

以下のような操作でいけそうな気がします。

  • まずは提示方法で補間(内挿)する
  • 補間前がnan かつ 補間後の前後の行の値のいずれかがnan である行を特定する
  • 上記の行なら補間後の値をnanに戻す。

以下、単純例での行の特定例です。

Python

1import pandas as pd 2import numpy as np 3nan = pd.np.nan 4 5l = [nan,nan,1,2,nan,3,nan] 6df1 = pd.DataFrame(l,index=pd.date_range('2016/01/01 00:00', freq='1Y', periods=len(l))) 7print(df1) 8""" 9 0 102016-12-31 NaN 112017-12-31 NaN 122018-12-31 1.0 132019-12-31 2.0 142020-12-31 NaN 152021-12-31 3.0 162022-12-31 NaN 17""" 18 19# まずは補間する 20df2 = df1.interpolate(limit=1, method="time", limit_area='inside', limit_direction="backward") 21print(df2) 22""" 23 0 242016-12-31 NaN 252017-12-31 NaN 262018-12-31 1.000000 272019-12-31 2.000000 282020-12-31 2.500684 292021-12-31 3.000000 302022-12-31 NaN 31""" 32 33# 補間前がnan かつ 補間後の前後の値のいずれかがnan 34sr = np.isnan(df1[0]) & ( np.isnan(df2[0].shift(-1)) | np.isnan(df2[0].shift(1)) ) 35print(sr) 36""" 372016-12-31 True 382017-12-31 True 392018-12-31 False 402019-12-31 False 412020-12-31 False 422021-12-31 False 432022-12-31 True 44""" 45 46# 上記の条件を満たす行にnanをセット 47df3 = df2.copy() 48df3[0][sr] = nan 49print(df3) 50""" 51 0 522016-12-31 NaN 532017-12-31 NaN 542018-12-31 1.000000 552019-12-31 2.000000 562020-12-31 2.500684 572021-12-31 3.000000 582022-12-31 NaN 59"""

上記の考えを元に提示例を変換した結果は以下になります。
色々なデータで結果が意図したものになるか確認ください。

Python

1import pandas as pd 2import numpy as np 3 4df1 = pd.DataFrame({"田中_身長":[129, pd.np.nan, 120, 133.4, 132, 150.5, 200.5], 5 "田中_体重":[29, 29.5, pd.np.nan, pd.np.nan, pd.np.nan, 50.5, 200], 6 "山田_身長":[180.6, 180.5, pd.np.nan, 177.3, 169, 170.2, 160.5], 7 "山田_体重":[pd.np.nan, 80.5, 81, pd.np.nan, 69, 70.2, 60.5]}, 8 index=pd.date_range('2016/01/01 00:00', freq='1Y', periods=7)) 9print(df1) 10 11df2 = df1.interpolate(limit=1, method="time", limit_area='inside', limit_direction="backward") 12print(df2) 13 14df3 = df2.copy() 15for c in df3.columns: 16 sr = np.isnan(df1[c]) & ( np.isnan(df2[c].shift(-1)) | np.isnan(df2[c].shift(1)) ) 17 df3[c][sr] = nan 18 19print(df3) 20""" 21 田中_身長 田中_体重 山田_身長 山田_体重 222016-12-31 129.0 29.0 180.6 NaN 232017-12-31 124.5 29.5 180.5 80.500000 242018-12-31 120.0 NaN 178.9 81.000000 252019-12-31 133.4 NaN 177.3 75.008208 262020-12-31 132.0 NaN 169.0 69.000000 272021-12-31 150.5 50.5 170.2 70.200000 282022-12-31 200.5 200.0 160.5 60.500000 29"""

投稿2019/08/25 02:07

編集2019/08/28 11:04
can110

総合スコア38266

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

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

rneko

2019/08/25 02:31

can110さん 具体例まで示してください,ありがとうございます. このようなやり方で目的が達成でき,目からうろこの状態です. 私が本来使いたかった,大きなデータでも無事に適用することができました.shiftとisnanの組み合わせでこんなことができるとは興味深いです. ループも列方向なので,使っていて気持ちが良いですし,コードも長くならず,大変勉強になりました. だいぶ長い間悩んでいたので,うれしいです!
guest

0

試していないですが、脳内で考えてみました。
for文なしで出来るかどうかは分かりません。

1.dataframeを複製して、shiftで↓方向に1行ずらします。
2.複製テーブルの1行目に全て1のデータを挿入します
3.オリジナルのテーブルと複製したテーブルを比較して、対応する項目の値が両方NaNなら複製したテーブルのNaNのままとします。複製したテーブルの方に実数が入っている場合は、1で上書きします
4.提示している方法で欠損値補完します
5.オリジナルのテーブルと複製したテーブルを掛け算します

投稿2019/08/25 01:58

pea

総合スコア426

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

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

rneko

2019/08/25 02:36

peaさん ご回答頂き,ありがとうございます. shiftとdeepcopyが味噌なのですね.なかなかこのようなテクニックを使うことができず,このような意見を頂けるととても勉強になります.興味深く面白いです. しっかりここら辺の関数を使いこなせるように頑張ります.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問