🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python

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

pandas

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

Q&A

解決済

3回答

811閲覧

Dataframe で特定の条件で対象行とその次の行のデータを削除する方法について

hiroyuki828

総合スコア22

Python

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

pandas

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

0グッド

0クリップ

投稿2019/12/23 11:58

編集2019/12/23 12:04

前提・実現したいこと

Python3.5 でPandas0.23.4 を利用しています。

Dataframe から特定の条件で見つかった行とその次の行を削除する
方法についてどのような方法が良いか、アドバイスいただけないでしょうか。

データ構造

python

1df = pd.DataFrame({ 2 'PCode' : [10011, 10012, 0, 10011, 10015,10016], 3 'PName' : ['Drink','Rice','TicketNo-123', 'Drink','Egg','Tea'], 4 'Price' : [1000, 1500, 0, 500, 1000, 1000]})

TicketNo-xxx の[xxx]については任意の数字(3桁~6桁)

判定条件

  1. TicketNo-xxx が存在する行(N)と次の行(N+1)の[PCode]が10011の場合、NとN+1の行は削除する
  2. TicketNo-xxx が存在する行(N)と次の行(N+1)の[PCode]が10011以外場合は、NとN+1は削除しない。

補足

  • 原則データの構造としては、TiketNo-xxx の次の行は、必ず[Pcode]が10011となる前提ですが、データの不備により10011以外になっている場合を検知したいので、10011以外の場合は削除しない条件を入れて、データの不備を確認したい事情があります。

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

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

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

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

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

guest

回答3

0

ベストアンサー

DataFrame.shift()を使うとよいのではないでしょうか

Python

1import pandas as pd 2 3df = pd.DataFrame({ 4 'PCode' : [10011, 10012, 0, 10011, 10015,10016], 5 'PName' : ['Drink','Rice','TicketNo-123', 'Drink','Egg','Tea'], 6 'Price' : [1000, 1500, 0, 500, 1000, 1000]}) 7 8 9target = df['PName'].str.startswith('TicketNo-') & (df['PCode'].shift(-1)==10011) 10df = df[~target & ~(target.shift(1).fillna(False))] 11print(df) 12# PCode PName Price 13#0 10011 Drink 1000 14#1 10012 Rice 1500 15#4 10015 Egg 1000 16#5 10016 Tea 1000

投稿2019/12/25 07:48

magichan

総合スコア15898

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

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

hiroyuki828

2019/12/25 10:42

ありがとうございます。 さまざまなアプローチがあることに驚きました。 こちらも実データで想定外のことがないかなど確認してみたいと思います。
hiroyuki828

2019/12/26 00:23

削除対象とする'TicketNo-xxx'のところに多少揺らぎがありまして 正規表現を使いたいのですが、正規表現でも同じように書けるのでしょうか? 揺らぎのパターンとしてはこのようなイメージです。 検索対象が文中に入る「山田様 TicketNo-120 にて販売」 Ticketの表記がない「No-121」
magichan

2019/12/26 00:34

であれば、 df['PName'].str.startswith('TicketNo-') の部分を df['PName'].str.contains('No-\d+') などに変更するとよいのではないでしょうか
hiroyuki828

2019/12/26 01:23

すみません。re を使おうとして悩んでました。 大変助かりました。ありがとうございます。
guest

0

Python

1is_ticket = df['PName'].str.startswith('TicketNo-').to_numpy() 2is_remove_pcode = df['PCode'].to_numpy() == 10011 3 4drop_index = np.all([is_ticket, np.concatenate((is_remove_pcode[1:], is_remove_pcode[-1:]))], 0).nonzero()[0] 5res = df.drop([*drop_index, *(drop_index+1)]) 6 7print(res) 8# PCode PName Price 9# 0 10011 Drink 1000 10# 1 10012 Rice 1500 11# 4 10015 Egg 1000 12# 5 10016 Tea 1000

こっちのほうがいいかも

Python

1is_ticket = df.loc[:, 'PName'].str.startswith('TicketNo-').to_numpy() 2is_remove_pcode = df.loc[:, 'PCode'].to_numpy() == 10011 3 4cond = np.r_[False, (is_ticket[:-1] & is_remove_pcode[1:]), False] 5res = df.loc[~(cond[1:] | cond[:-1])]

投稿2019/12/24 01:34

編集2019/12/24 07:47
kirara0048

総合スコア1399

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

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

hiroyuki828

2019/12/24 04:22

ありがとうございます。 to_numpy() が動かず悩みましたが pandas 0.23.4 →0.25.3 にすることで解決できました。 実際のデータに適用できるようコードを書いてみます。
kirara0048

2019/12/24 04:36

`.to_numpy()`が実装される前のバージョンの場合は、`.to_numpy()`を`.values`に置き換えてください。
hiroyuki828

2019/12/25 10:35

補足ありがとうございます。メソッドの意味を勉強していきたいと思います。
hiroyuki828

2019/12/25 10:53

別コードありがとうございます。さまざまなアプローチ方法がありとても参考になります。
guest

0

こんな感じでどうでしょうか?
すぐには同じ環境が用意できないので、Python 3.7.3, Pandas 0.24.2 で試しましたが、たぶんご質問の環境でも大丈夫かとおもいます。

python

1# PName が TicketNo- で始まるものを削除候補の Ticket と見做す。 2df['is_Ticket'] = df['PName'].map(lambda x: x.startswith('TicketNo-')) 3 4# 次の行の PCode を保持する列を追加する。後の削除判定で使う。 5df['next_PCode'] = df['PCode'][1:].append(pd.Series([0]), ignore_index=True) 6 7# 削除対象の Ticket を判定。 8df['is_remove_Ticket'] = pd.Series(zip(df['is_Ticket'], df['next_PCode'])).map(lambda x: x[0] and x[1] == 10011) 9 10# 前の行が削除対象 Ticket か判定 11df['is_prev_remove_Ticket'] = pd.Series([False]).append(df['is_remove_Ticket'][:-1], ignore_index=True) 12 13# 削除対象 Ticket を削除 14result = df[df['is_remove_Ticket'] != True] 15 16# 削除対象 Ticket の次の行を削除 17result = result[result['is_prev_remove_Ticket'] != True]

追記

削除対象の Ticket を判定する箇所は、zip() を使わず次のようにするほうがスマートでした。

python

1# 削除対象の Ticket を判定。 2df['is_remove_Ticket'] = df[['is_Ticket', 'next_PCode']].apply(lambda x: x[0] and x[1] == 10011, axis=1)

投稿2019/12/23 14:24

編集2019/12/24 01:45
kit494way

総合スコア317

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

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

hiroyuki828

2019/12/24 04:08

このようにDataframeを操作できることに大変驚きました。 ありがとうございます。 取り急ぎ zip() を使わないほうはサンプルで動かせましたので 実際のデータ構造にあわせてコードを書いてみたいと思います。 zip() 使う場合は私の力量不足で何かの定義がおかしいのか TypeError: object of type 'zip' has no len() となり動かせませんでした。
hiroyuki828

2019/12/24 04:24

zip() 使うパターンも pandas 0.23.4 →0.25.3 にすることで解決しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問