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

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

ただいまの
回答率

90.53%

  • Python

    11314questions

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

  • pandas

    855questions

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

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

解決済

回答 2

投稿

  • 評価
  • クリップ 2
  • VIEW 2,996

pepasuke623

score 34

前提・実現したいこと

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

次の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列をサブルーチンに渡すことが出来ないみたいなので(これも間違っていたら済みません)途方にくれてしまいました。

import pandas as pd

# パーサーを定義する
my_parser = lambda x: pd.datetime.strptime(x, '%H:%M:%S')

# ファイルを読み込む
df_mach = pd.read_csv('machine.csv', date_parser=my_parser)
df_mate = pd.read_csv('material.csv', date_parser=my_parser)

def func(valu):
#######
# ここの書き方が分からない
#######
    return ##

df_mach['result'] =  df_mach['time'].map(func)

print(df_mach)


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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

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

import pandas as pd

# パーサーを定義する
my_parser = lambda x: pd.datetime.strptime(x, '%H:%M:%S')

# ファイルを読み込む
df_mach = pd.read_csv('machine.csv', date_parser=my_parser)
df_mate = pd.read_csv('material.csv', date_parser=my_parser)

# 検索用に DateTime 型の列を追加した material を作成する
df_mate_ = df_mate.assign(time_=lambda s: pd.to_datetime(s.time))

def func(row):
    # DateTime 型に変換する
    time_ = pd.to_datetime(row.time)
    # 条件が一致する material を探す
    matches = df_mate_[(df_mate_.machine == row.machine) &
                       (df_mate_.time_ >= time_) &
                       (df_mate_.time_ <= (time_ + pd.tseries.offsets.Minute(5)))]
    # 一致する場合は、一番近い time を返す
    return None if matches.empty else matches.sort_values('time_').time.iloc[0]

df_mach['result'] = df_mach.apply(func, axis='columns')

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/29 22:00

    ご回答ありがとうございます!!

    頂いたコードを実行してみたら
    "'second must be in 0..59', 'occurred at index 7'"
    とメッセージが出てきました。

    他の列も「pd.to_datetime」で変換しないとエラーなるのか?と
    一時間くらい試行錯誤しましたが結局分かりませんでした・・・

    私が初歩的なことを分かっていないだけな気がしますが,
    自分で気づくのが難しそうなので,お手数ですがご教示いただければと思います。

    ちなみに環境はSpyder 3.1.2 のPython 3.5.2です

    キャンセル

  • 2017/01/29 23:17

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

    キャンセル

  • 2017/02/02 21:30

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

    キャンセル

+1

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

下記の条件ですが

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

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

import pandas as pd

df_mach = pd.read_csv('machine.csv')
df_mate = pd.read_csv('material.csv')


def func(machine_machine, machine_time, df):
    # 時刻の計算ができるようにdatetime型に変換
    machine_time = pd.to_datetime(machine_time)
    datetime_df = df.copy()
    datetime_df['time'] = pd.to_datetime(datetime_df['time'])

    # 機械名でフィルタ
    machine_df = datetime_df[datetime_df['machine'] == machine_machine].copy()

    # machine.csvより後のデータ
    machine_df = machine_df.loc[machine_df['time'].map(
        lambda x: (x - machine_time).value) > 0]

    # machine.csvより後のデータがなかったらNoneを返す
    if not len(machine_df):
        return None

    # machine.csvより後のデータでmachine.csvの時間差が最も小さいデータ
    min_time = machine_df.loc[machine_df['time'].map(
        lambda x: (x - machine_time).value).idxmin(), 'time']

    # 上記の該当した時間差が5分以内であれば時間差が最も小さいデータを返す
    if (min_time - machine_time).value <= 300000000000:
        return df.loc[datetime_df[datetime_df['time'] == min_time].index[0],
                      'time']
    else:
        return None


df_mach['result'] = df_mach.apply(
    lambda x: func(x['machine'], x['time'], df_mate), axis=1)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/29 22:07 編集

    ご回答ありがとうございます!!

    そして、上の方と同じく、頂いたコードを実行してみたら
    "'second must be in 0..59', 'occurred at index 7'"
    とメッセージが出てきました。

    上の方のコードと同じく、思い当たるところを試行錯誤しましたが結局分かりませんでした・・・

    キャンセル

同じタグがついた質問を見る

  • Python

    11314questions

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

  • pandas

    855questions

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