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

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

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

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

pandas

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

Q&A

解決済

2回答

8618閲覧

Python:2つのデータフレームを比較し,時間が近いものを抜き出す

pepasuke623

総合スコア55

Python

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

pandas

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

0グッド

2クリップ

投稿2017/01/28 01:37

##前提・実現したいこと
いつもお世話になっております。データフレームの取扱が分からず、質問させていただきます。

次の2つのCSVファイルがあるとします。
(本当はソースコードの中でデータフレームとして定義した方が答えやすいのですが,
pandas.DataFrameからdate_parserが呼び出せない?(違ったらスミマセン)ので
CSVからデータフレームを作成したことにしています)

・machine.csv
次のものが機械が特定の動作をする時間を記録したものとします。
左のmachine列が機械の種類、time列が特定の動作をする時間です。

//machine.csv machine,time A,10:00:00 A,10:00:05 A,10:00:10 A,10:00:15 A,10:00:25 B,10:00:05 B,10:00:15 B,10:00:20

・material.csv
次のCSVが材料の投入時間を記録したものです。
time列が投入する時間,machine列が投入先の機械です。

//material.csv time,machine 10:00:01,A 10:00:07,A 10:00:09,B 10:00:15,B 10:00:24,B

ここで実現したい事がやや複雑なので箇条書します

  • machine.csvにresult列を追加する
  • machine.csvのtime列とmaterial.csvの各行のtime列を比較し、
  • 「material.csvの時間がmachine.csvの時間より後で一番近い時間」
  • 且つ「material.csvの時間とmachine.csvの時間が5分以内」
  • 且つ「機械の種類が一致する」

ものがあればresult列にmaterial.csvの時間を書き込む

  • 条件に合うものがなければNonを代入する

という操作をしたいと思っています。
アウトプットは次のようになります。

//RESULT DataFrame machine,time,result A 10:00:00 10:00:01 A 10:00:05 10:00:07 A 10:00:10 Non A 10:00:15 Non A 10:00:25 Non B 10:00:05 10:00:09 B 10:00:15 10:00:15 B 10:00:20 10:00:24

Forループを物凄い駆使すれば出来なくはないかもしれませんが、ちょっと大変です。
良いテクニックがあればご教示おねがいします。

##試したこと
applyやmapを使って出来ないかと試行錯誤しましたが,applyやmapは2列をサブルーチンに渡すことが出来ないみたいなので(これも間違っていたら済みません)途方にくれてしまいました。

python

1import pandas as pd 2 3# パーサーを定義する 4my_parser = lambda x: pd.datetime.strptime(x, '%H:%M:%S') 5 6# ファイルを読み込む 7df_mach = pd.read_csv('machine.csv', date_parser=my_parser) 8df_mate = pd.read_csv('material.csv', date_parser=my_parser) 9 10def func(valu): 11####### 12# ここの書き方が分からない 13####### 14 return ## 15 16df_mach['result'] = df_mach['time'].map(func) 17 18print(df_mach)

結局Forループなどを駆使して複雑な処理をしないと実現できないのでしょうか?

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

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

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

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

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

guest

回答2

0

ベストアンサー

DataFrame.apply の axis 引数に 'columns' (または 1) を指定すると、関数には行の Series が渡ってきます。
また、DataFrame の [] には、条件を記述して絞り込み検索することができます。

Python

1import pandas as pd 2 3# パーサーを定義する 4my_parser = lambda x: pd.datetime.strptime(x, '%H:%M:%S') 5 6# ファイルを読み込む 7df_mach = pd.read_csv('machine.csv', date_parser=my_parser) 8df_mate = pd.read_csv('material.csv', date_parser=my_parser) 9 10# 検索用に DateTime 型の列を追加した material を作成する 11df_mate_ = df_mate.assign(time_=lambda s: pd.to_datetime(s.time)) 12 13def func(row): 14 # DateTime 型に変換する 15 time_ = pd.to_datetime(row.time) 16 # 条件が一致する material を探す 17 matches = df_mate_[(df_mate_.machine == row.machine) & 18 (df_mate_.time_ >= time_) & 19 (df_mate_.time_ <= (time_ + pd.tseries.offsets.Minute(5)))] 20 # 一致する場合は、一番近い time を返す 21 return None if matches.empty else matches.sort_values('time_').time.iloc[0] 22 23df_mach['result'] = df_mach.apply(func, axis='columns')

投稿2017/01/28 19:46

編集2017/01/28 20:04
copepoda

総合スコア324

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

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

pepasuke623

2017/01/29 13:00

ご回答ありがとうございます!! 頂いたコードを実行してみたら "'second must be in 0..59', 'occurred at index 7'" とメッセージが出てきました。 他の列も「pd.to_datetime」で変換しないとエラーなるのか?と 一時間くらい試行錯誤しましたが結局分かりませんでした・・・ 私が初歩的なことを分かっていないだけな気がしますが, 自分で気づくのが難しそうなので,お手数ですがご教示いただければと思います。 ちなみに環境はSpyder 3.1.2 のPython 3.5.2です
copepoda

2017/01/29 14:17

エラー内容は「秒は0~59でなければなりません、インデックス 7 (8行目)で発生しました」と言っています。 machine.csv の8行目の time 列はどのような値が設定されていますか。 "10:00:60" など、秒が0~59の範囲外になっていませんか。
pepasuke623

2017/02/02 12:30

ありがとうございます! 出来ました!!
guest

0

元のデータでは時刻の計算がやりにくいため、関数の内部でdatetime型に変換してみました。

下記の条件ですが

  • 「material.csvの時間がmachine.csvの時間より後で一番近い時間」

この場合はB 10:00:15の出力例が異なると思いますがいかがでしょうか?

python

1import pandas as pd 2 3df_mach = pd.read_csv('machine.csv') 4df_mate = pd.read_csv('material.csv') 5 6 7def func(machine_machine, machine_time, df): 8 # 時刻の計算ができるようにdatetime型に変換 9 machine_time = pd.to_datetime(machine_time) 10 datetime_df = df.copy() 11 datetime_df['time'] = pd.to_datetime(datetime_df['time']) 12 13 # 機械名でフィルタ 14 machine_df = datetime_df[datetime_df['machine'] == machine_machine].copy() 15 16 # machine.csvより後のデータ 17 machine_df = machine_df.loc[machine_df['time'].map( 18 lambda x: (x - machine_time).value) > 0] 19 20 # machine.csvより後のデータがなかったらNoneを返す 21 if not len(machine_df): 22 return None 23 24 # machine.csvより後のデータでmachine.csvの時間差が最も小さいデータ 25 min_time = machine_df.loc[machine_df['time'].map( 26 lambda x: (x - machine_time).value).idxmin(), 'time'] 27 28 # 上記の該当した時間差が5分以内であれば時間差が最も小さいデータを返す 29 if (min_time - machine_time).value <= 300000000000: 30 return df.loc[datetime_df[datetime_df['time'] == min_time].index[0], 31 'time'] 32 else: 33 return None 34 35 36df_mach['result'] = df_mach.apply( 37 lambda x: func(x['machine'], x['time'], df_mate), axis=1)

投稿2017/01/28 05:50

編集2017/01/28 05:51
driller

総合スコア720

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

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

pepasuke623

2017/02/02 12:30 編集

ご回答ありがとうございます!! そして、上の方と同じく、頂いたコードを実行してみたら "'second must be in 0..59', 'occurred at index 7'" とメッセージが出てきました。 上の方のコードと同じく、思い当たるところを試行錯誤しましたが結局分かりませんでした・・・
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問