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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

最適化

最適化とはメソッドやデザインの最適な処理方法を選択することです。パフォーマンスの向上を目指す為に行われます。プログラミングにおける最適化は、アルゴリズムのスピードアップや、要求されるリソースを減らすことなどを指します。

Python

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

pandas

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

Q&A

解決済

3回答

898閲覧

開始と終了レコードを結合するpythonスクリプトを最適化したい

quinty

総合スコア17

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

最適化

最適化とはメソッドやデザインの最適な処理方法を選択することです。パフォーマンスの向上を目指す為に行われます。プログラミングにおける最適化は、アルゴリズムのスピードアップや、要求されるリソースを減らすことなどを指します。

Python

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

pandas

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

0グッド

1クリップ

投稿2021/10/22 17:36

前提・実現したいこと

集計用にログの開始レコードと終了レコードを結合するスクリプトを作成しました。

もっと最適な実現方法があるのではないかと思うのですが
知識不足のため思いつきません。

みなさまのベストプラクティスを教えて頂ければ幸いです。
ライブラリなどの指定はありません。
よろしくお願いいたします。

該当のソースコード

python

1 2## INPUT 3 name status dtime 40 siro ST 2020-01-01 00:00:05 51 siro ST 2020-01-01 00:00:10 62 siro ED 2020-01-01 00:00:15 73 eiro ST 2020-01-02 00:00:20 84 eiro ST 2020-01-02 00:00:25 95 eiro ED 2020-01-02 00:00:30 106 airo ED 2020-01-03 00:00:35 117 kkkk ST 2020-01-04 01:10:10 128 eiro ST 2020-01-05 02:10:20 139 kkkk ED 2020-01-05 01:10:30 1410 eiro ED 2020-01-05 02:20:30 15 16## OUTPUT 17 name status1 dtime1 status2 dtime2 180 siro ST 2020-01-01 00:00:05 None None 191 siro ST 2020-01-01 00:00:10 ED 2020-01-01 00:00:15 202 eiro ST 2020-01-02 00:00:20 None None 213 eiro ST 2020-01-02 00:00:25 ED 2020-01-02 00:00:30 224 airo None None ED 2020-01-03 00:00:35 235 kkkk ST 2020-01-04 01:10:10 ED 2020-01-05 01:10:30 246 eiro ST 2020-01-05 02:10:20 ED 2020-01-05 02:20:30 25 26## 現状のプログラム 27import pandas as pd 28data=[ 29 ['siro','ST','2020-01-01 00:00:05'] 30 ,['siro','ST','2020-01-01 00:00:10'] 31 ,['siro','ED','2020-01-01 00:00:15'] 32 ,['eiro','ST','2020-01-02 00:00:20'] 33 ,['eiro','ST','2020-01-02 00:00:25'] 34 ,['eiro','ED','2020-01-02 00:00:30'] 35 ,['airo','ED','2020-01-03 00:00:35'] 36 ,['kkkk','ST','2020-01-04 01:10:10'] 37 ,['eiro','ST','2020-01-05 02:10:20'] 38 ,['kkkk','ED','2020-01-05 01:10:30'] 39 ,['eiro','ED','2020-01-05 02:20:30'] 40 41] 42cols = ['name', 'status1','dtime1','status2','dtime2'] 43odf = pd.DataFrame(index=[], columns=cols) 44 45for i,row in df.iterrows(): 46 df.loc[i,'p']=False 47 48for i,row in df.iterrows(): 49 50 if row.status == 'ST': 51 df.loc[i,'p']=True 52 53 for j,row2 in df[i+1:].iterrows(): 54 if row2.name==row2.name: 55 df.loc[j,'p']=True 56 if row2.status=='ST': 57 record = pd.Series([row.name, row.status, row.dtime,None,None], index=odf.columns) 58 odf = odf.append(record, ignore_index=True) 59 break 60 61 if row2.status=='ED': 62 record = pd.Series([row.name, row.status, row.dtime,row2.status,row2.dtime], index=odf.columns) 63 odf = odf.append(record, ignore_index=True) 64 break 65 66 if (row['p']==True): 67 continue 68 69 if row.status=='ED': 70 df.loc[i,'p']=True 71 record = pd.Series([row.name,None,None, row.status, row.dtime], index=odf.columns) 72 odf = odf.append(record, ignore_index=True) 73 74

補足情報(FW/ツールのバージョンなど)

python 3.6
ライブラリなどの指定はありません。

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

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

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

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

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

guest

回答3

0

ベストアンサー

nameでグループ化して、ST毎にカウントアップする列を作れば、その値を使ってSTに対応するEDを取り出せます。
この処理はgroupbyとcumsumでできます。

nameでグループ化して、statusをカウントアップして、EDの場合はそこから1ひく処理をすれば、その値を使ってSTに対応するEDを取り出せます。
(最初の回答だとEDが連続するケースがあった場合が違う出力になるので修正しました。そういうケースがないならどちらでもいいです。)

python

1import pandas as pd 2 3df = pd.DataFrame(data=[ 4 ['siro','ST','2020-01-01 00:00:05'] 5 ,['siro','ST','2020-01-01 00:00:10'] 6 ,['siro','ED','2020-01-01 00:00:15'] 7 ,['eiro','ST','2020-01-02 00:00:20'] 8 ,['eiro','ST','2020-01-02 00:00:25'] 9 ,['eiro','ED','2020-01-02 00:00:30'] 10 ,['airo','ED','2020-01-03 00:00:35'] 11 ,['kkkk','ST','2020-01-04 01:10:10'] 12 ,['eiro','ST','2020-01-05 02:10:20'] 13 ,['kkkk','ED','2020-01-05 01:10:30'] 14 ,['eiro','ED','2020-01-05 02:20:30'] 15], columns=['name', 'status','dtime']) 16 17df['n'] = df.groupby('name')['status'].cumcount() - (df['status'] == 'ED') 18#df['n'] = (df['status'] == 'ST').groupby(df['name']).cumsum()

あとは、これを使ってpivotかmergeをすればいいですが、欲しい形に近くなるのはmergeでしょう。
STだけのデータフレーム、EDだけのデータフレームを作って、nameと上で作った列でouter mergeします。

  • STがないairoの行が下になってしまいます
  • NoneではなくNaNが入っています

python

1odf = pd.merge( 2 df[df['status'] == 'ST'], df[df['status'] == 'ED'], 3 on=['name', 'n'], how='outer', suffixes=('1', '2')) 4odf = odf.drop(columns='n')

dtime1がない行をdtime2の値を使うようにしてソートします。

python

1sort_idx = odf['dtime1'].fillna(odf['dtime2']).sort_values().index 2odf = odf.reindex(sort_idx).reset_index(drop=True) 3# name status1 dtime1 status2 dtime2 4#0 siri ST 2020-01-01 00:00:05 NaN NaN 5#1 siro ST 2020-01-01 00:00:10 ED 2020-01-01 00:00:15 6#2 eiro ST 2020-01-02 00:00:20 NaN NaN 7#3 eiro ST 2020-01-02 00:00:25 ED 2020-01-02 00:00:30 8#4 airo NaN NaN ED 2020-01-03 00:00:35 9#5 kkkk ST 2020-01-04 01:10:10 ED 2020-01-05 01:10:30 10#6 eiro ST 2020-01-05 02:10:20 ED 2020-01-05 02:20:30

投稿2021/10/23 05:51

編集2021/10/25 02:08
bsdfan

総合スコア4520

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

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

quinty

2021/10/23 14:40

ご返答ありがとうございます。 試させてもらいます。
lehshell

2021/10/24 13:46

index まで合わせると気持ちいいと思います。 odf = odf.reset_index().drop(columns='index')
lehshell

2021/10/24 13:50

odf = odf.reset_index(drop=True) にすべきでした。m(__)m
bsdfan

2021/10/25 02:09

lehshellさん reset_index()はどっちでもいいかな、と思ったのですが、しておいたほうが気持ちはいいですね。
quinty

2021/10/25 14:30 編集

これ以上ないシンプルな解法かと思いました。 もしよろしければ結合キーにver項を加えた場合の解法を教えて頂けないでしょうか。 この辺でエラーが出ます。 df['n'] = (df['status'] == 'ST').groupby(df['name','ver']).cumsum()
bsdfan

2021/10/25 23:45

これだけの情報ではちょっとわからないところですが、 groupby([df['name'], df['ver']]) でどうでしょう。 あとは、mergeのときのonにも'ver'を追加したらいいはずです。 これでダメなら別質問に挙げていただいた方がいいかも。
quinty

2021/10/26 13:57 編集

上手くいきました。ありがとうございました。
guest

0

for文を使わないでやると、以下のようになります。
性能は良いと思いますが、理解しやすいかどうかは人によります。

python

1df_sort = df.sort_values(['name', 'dtime']) 2df_sort_prev = df_sort.shift(1) 3df_sort['start_end'] = (df_sort['name']==df_sort_prev['name']) & (df_sort['status']=='ED') & (df_sort_prev['status']=='ST') 4df_end = df_sort[['status','dtime']].where(df_sort['start_end']==True).shift(-1) 5df_end[['status', 'dtime']] = df_sort[['status', 'dtime']].where((df_sort['start_end']==False) & (df_sort['status']=='ED'), df_end[['status', 'dtime']]) 6df_concat = pd.concat([df_sort, df_end], axis=1) 7df_concat.columns = ['name', 'status1', 'dtime1', 'start_end', 'status2', 'dtime2'] 8df_result = df_concat[df_concat['start_end']==False].drop('start_end', axis=1).sort_index()

入力データ

python

1>>> print(df) 2 name status dtime 30 siro ST 2020-01-01 00:00:05 41 siro ST 2020-01-01 00:00:10 52 siro ED 2020-01-01 00:00:15 63 eiro ST 2020-01-02 00:00:20 74 eiro ST 2020-01-02 00:00:25 85 eiro ED 2020-01-02 00:00:30 96 airo ED 2020-01-03 00:00:35 107 kkkk ST 2020-01-04 01:10:10 118 eiro ST 2020-01-05 02:10:20 129 kkkk ED 2020-01-05 01:10:30 1310 eiro ED 2020-01-05 02:20:30

実行結果

python

1>>> print(df_result) 2 name status1 dtime1 status2 dtime2 30 siro ST 2020-01-01 00:00:05 NaN NaN 41 siro ST 2020-01-01 00:00:10 ED 2020-01-01 00:00:15 53 eiro ST 2020-01-02 00:00:20 NaN NaN 64 eiro ST 2020-01-02 00:00:25 ED 2020-01-02 00:00:30 76 airo ED 2020-01-03 00:00:35 ED 2020-01-03 00:00:35 87 kkkk ST 2020-01-04 01:10:10 ED 2020-01-05 01:10:30 98 eiro ST 2020-01-05 02:10:20 ED 2020-01-05 02:20:30

投稿2021/10/24 06:59

ppaul

総合スコア24666

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

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

quinty

2021/10/24 23:51

ご返答ありがとうございます。 試させてもらいます。
guest

0

実行時間などは比較していないのでどれほど最適化できたかは不明ですが、できるだけforループを使わないようにするとよいかと思います。

Python

1import pandas as pd 2 3data=[ 4 ['siro','ST','2020-01-01 00:00:05'] 5 ,['siro','ST','2020-01-01 00:00:10'] 6 ,['siro','ED','2020-01-01 00:00:15'] 7 ,['eiro','ST','2020-01-02 00:00:20'] 8 ,['eiro','ST','2020-01-02 00:00:25'] 9 ,['eiro','ED','2020-01-02 00:00:30'] 10 ,['airo','ED','2020-01-03 00:00:35'] 11 ,['kkkk','ST','2020-01-04 01:10:10'] 12 ,['eiro','ST','2020-01-05 02:10:20'] 13 ,['kkkk','ED','2020-01-05 01:10:30'] 14 ,['eiro','ED','2020-01-05 02:20:30'] 15] 16cols = ['name', 'status','dtime'] 17df = pd.DataFrame(data, columns=cols) 18 19last = {} 20def func(row): 21 idx = -1 22 if row['status'] == 'ED': 23 last[row['name']] = row.name 24 else: 25 idx = last.pop(row['name'], idx) 26 # 終了行に、対応する開始行の位置をセット 27 if idx >= 0: 28 df.loc[idx, 'st'] = row.name 29 30# 逆順に走査して終了行の直近の開始行を決定 31df['st'] = -1 32df.iloc[::-1].apply(func, axis=1) 33 34# 開始、終了行同士を結合 35df = pd.merge(df[df['status'] == 'ST'], df[df['status'] == 'ED'], left_index=True, right_on='st', how='outer', suffixes=('1','2')) 36 37# 開始行のない終了行に必要な情報をセットして元レコード順に並び替え 38df = df.reset_index() 39rows = df['st'] < 0 40df.loc[rows,'st'] = df.loc[rows,'index'] 41df.loc[rows,'name1'] = df.loc[rows,'name2'] 42df = df.sort_values(['st']) 43 44# 必要な列のみ 45df = df.loc[:,['name1','status1','dtime1','status2','dtime2']] 46print(df) 47""" 48 name1 status1 dtime1 status2 dtime2 490 siro ST 2020-01-01 00:00:05 NaN NaN 501 siro ST 2020-01-01 00:00:10 ED 2020-01-01 00:00:15 512 eiro ST 2020-01-02 00:00:20 NaN NaN 523 eiro ST 2020-01-02 00:00:25 ED 2020-01-02 00:00:30 536 airo NaN NaN ED 2020-01-03 00:00:35 544 kkkk ST 2020-01-04 01:10:10 ED 2020-01-05 01:10:30 555 eiro ST 2020-01-05 02:10:20 ED 2020-01-05 02:20:30 56"""

投稿2021/10/23 03:45

can110

総合スコア38233

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

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

quinty

2021/10/23 14:40

ご返答ありがとうございます。 試させてもらいます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問