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

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

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

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

Q&A

解決済

2回答

2092閲覧

重複する複数の期間をまとめたいです(doube countを無くしたいです)

pickle

総合スコア16

Python

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

1グッド

2クリップ

投稿2020/04/30 13:48

python

1df = pd.DataFrame({"ID":[0, 0, 1, 1, 1, 2, 2, 2, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 8], 2 "drug":['A', 'B', 'C', 'C', 'B', 'D', 'D', 'E', 'D', 'C', 'D', 'E', 'D', 'E', 'C', 'B', 'A', 'F', 'B', 'B'], 3 "begin":['2018-12-26', '2019-01-25', '2014-09-02', '2014-12-02', '2014-12-23', '2014-01-10', '2016-01-25', '2015-12-18', '2014-01-27', '2015-01-15', '2015-02-04', '2016-04-26', '2014-02-06', '2014-10-07', '2016-08-19', '2016-08-23', '2014-01-01', '2014-01-27', '2014-02-07', '2014-08-27'], 4 "end":['2019-01-23', '2019-02-07', '2014-09-15', '2015-01-08', '2015-02-05', '2015-12-27', '2020-04-06', '2016-01-24', '2020-02-17', '2015-02-03', '2016-04-25', '2020-03-31', '2014-10-06', '2016-08-22', '2016-08-29', '2017-03-21', '2017-09-19', '2014-02-07', '2014-04-08', '2014-09-08'] 5 }) 6df["begin"] = pd.to_datetime(df["begin"]) 7df["end"] = pd.to_datetime(df["end"]) 8df["administer_period"] = (df["end"]+datetime.timedelta(days=1) - df["begin"]) 9df 10# 上から5行をpick up 11 ID drug begin end administer_period 120 0 A 2018-12-26 2019-01-23 29 days 131 0 B 2019-01-25 2019-02-07 14 days 142 1 C 2014-09-02 2014-09-15 14 days 153 1 C 2014-12-02 2015-01-08 38 days 164 1 B 2014-12-23 2015-02-05 45 days

上の様に個人番号("ID"), 使用した薬("drug")、薬の開始日("begin")、薬の終了日("end")、がdataframezで与えられてます。administer_periodはend+1days-begin,で導出しています。

例えば、ID1の人をみてみると、drugCとdrugBの使用期間が 20141223-20150108 でかぶってます。
**
そういったdouble countを排除して、その人に結局いずれかの薬が投与された総日数を求めたいです。**
(例えばID1の人では、double countしている日数が17日あるので、(14+38+45)-17 = 80日、となります)

なんとかして上記目標を達成するcodeが書けないでしょうか?
試行錯誤してみたのですが、3つ以上の薬が重複するケースなども存在し、うまいcodeが思いつきません。
(ID5のケースなど)

複雑になるかと思いますが、分かります方がいらっしゃいましたら、ご教授頂けますと幸いです。
よろしくお願い申し上げます。

DrqYuto👍を押しています

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

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

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

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

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

guest

回答2

0

pandas不慣れですが、挑戦してみました。

timedeltaでは重複する日の判定ができないので、
pandas.date_range → 日付のリストに展開してから重複する日を削除。

コードはpandas内で完結できるような、もっと良い書き方があるかもしれません。

python

1import itertools 2import pandas as pd 3 4df = pd.DataFrame({"ID":[0, 0, 1, 1, 1, 2, 2, 2, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 8], 5 "drug":['A', 'B', 'C', 'C', 'B', 'D', 'D', 'E', 'D', 'C', 'D', 'E', 'D', 'E', 'C', 'B', 'A', 'F', 'B', 'B'], 6 "begin":['2018-12-26', '2019-01-25', '2014-09-02', '2014-12-02', '2014-12-23', '2014-01-10', '2016-01-25', '2015-12-18', '2014-01-27', '2015-01-15', '2015-02-04', '2016-04-26', '2014-02-06', '2014-10-07', '2016-08-19', '2016-08-23', '2014-01-01', '2014-01-27', '2014-02-07', '2014-08-27'], 7 "end":['2019-01-23', '2019-02-07', '2014-09-15', '2015-01-08', '2015-02-05', '2015-12-27', '2020-04-06', '2016-01-24', '2020-02-17', '2015-02-03', '2016-04-25', '2020-03-31', '2014-10-06', '2016-08-22', '2016-08-29', '2017-03-21', '2017-09-19', '2014-02-07', '2014-04-08', '2014-09-08'] 8 }) 9 10df1 = df.query("ID==1").apply(lambda x: pd.date_range(start=x.begin, end=x.end, freq="D"), 1) 11days = len(set(itertools.chain.from_iterable(df1))) 12 13print(days) # => 80

投稿2020/05/01 03:08

teamikl

総合スコア8760

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

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

pickle

2020/05/01 11:23

> teamikiさん ご回答ありがとうございます! pd.data_rangeというmoduleについて知らず、勉強になりました。 被らない日付をset関数で取り出して、lenで数える、との方法、非常に簡潔で分かりやすかったです。 やりたいことがまさにできました^^ ありがとうございます、今後とも何卒よろしくお願いいたします。
guest

0

ベストアンサー

ジャストアイデアですが。。

groupby() を使用してID毎に得られた複数の行をに対して

  1. pandas.period_range() にて PeriodIndex化する
  2. 上記のPeriodIndex 同士を pandas.Index.union() にて重複を除いて結合する
  3. 全てのIndexが結合されたら、サイズを取得する

を行うのはどうでしょう。

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.period_range.html
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.PeriodIndex.html
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Index.union.html

下記がサンプルコードですが、それっぽい値が返ってきております。
(検算未実施・・スミマセン)

Python

1def f(d): 2 # とりあえず空のPeriodIndexを作る 3 idx = pd.PeriodIndex([], freq='d') 4 # 行毎にループ 5 for _,row in d.iterrows(): 6 # PeriodIndexを作成して、結合(重複は削除) 7 idx = idx.union(pd.period_range(row['begin'], row['end'], freq='d')) 8 return idx.size 9 10ret = df.groupby(['ID']).apply(f) 11print(ret) 12#ID 13#0 43 14#1 80 15#2 2279 16#3 2213 17#4 1903 18#5 1358 19#6 72 20#8 13 21#dtype: int64

投稿2020/05/01 03:04

magichan

総合スコア15898

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

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

teamikl

2020/05/01 03:24

便乗で、学習の題材として挑戦してたのですが、 unionでの結合の仕方が解らず悩んでました。 空のindex作成すればよかったんですね。参考になりました感謝
pickle

2020/05/01 11:58

> magichanさん いつも本当にありがとうございます。 teamikiさんがおっしゃってる様に空のPeriodIndexを作成しておかないとうまく回らないのですね。 自分がやりたいことをsimpleにまとめていただきありがとうございました^ ^ 非常に複雑に考えて行き詰まっていたのですが、これほどsimpleにかけるとは思いませんでした。 今後とも何卒よろしくお願いいたします。
pickle

2020/05/01 11:58

> magichanさん いつも本当にありがとうございます。 teamikiさんがおっしゃってる様に空のPeriodIndexを作成しておかないとうまく回らないのですね。 自分がやりたいことをsimpleにまとめていただきありがとうございました^ ^ 非常に複雑に考えて行き詰まっていたのですが、これほどsimpleにかけるとは思いませんでした。 今後とも何卒よろしくお願いいたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問