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

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

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

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

Q&A

解決済

1回答

15848閲覧

2つのデータフレームを比較して、ある時間に該当するものを重複を削除した上で出力する:Pandas

pepasuke623

総合スコア55

pandas

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

0グッド

2クリップ

投稿2017/05/20 13:23

###前提・実現したいこと
お世話になります。以前の質問から派生して、下記のようなことを実現したいと思います。

  • 機械が稼働する時間のデータ(machine.csv)とある時間に投入される材料データ(material.csv)がある
  • (machine.csv)の['machine']はその時間に稼働している機械のID,['time']はその時間を意味している
  • 材料データ(material.csv)の['time']は材料が投入される時間,['machine']は投入される機械,['material']は投入される材料のIDを意味している
  • machine.csvのデータ(データフレーム内の['machine'])と時間(データフレーム内の['time'])に対応する材料のIDを抜き出す
  • ただし,機械が稼働する時間から後5秒に投入される材料をすべて列挙する。例えば(machine.csv)のA,10:00:00に対応する(material.csv)は,10:00:00,A,{'id': 'x001'},10:00:01,A,{'id': 'x001'}。ただし材料IDが重複するので削除したい

csv

1//machine.csv 2machine,time 3A,10:00:00 4A,10:00:02 5C,10:00:03 6C,10:00:04 7A,10:00:05 8B,10:00:08 9B,10:00:10 10B,10:00:12 11A,10:00:15 12A,10:00:16 13A,10:00:17 14A,10:00:18 15B,10:00:19 16B,10:00:20 17B,10:00:22
//material.csv time,machine,material 10:00:00,A,{'id': 'x001'} 10:00:01,A,{'id': 'x001'} 10:00:03,B,{'id': 'x002'} 10:00:05,B,{'id': 'x002'} 10:00:06,B,{'id': 'x001'} 10:00:08,A,{'id': 'x001'} 10:00:09,A,{'id': 'x001'} 10:00:11,B,{'id': 'x003'} 10:00:12,B,{'id': 'x003'} 10:00:13,B,{'id': 'x003'} 10:00:14,A,{'id': 'x001'} 10:00:16,A,{'id': 'x003'} 10:00:18,B,{'id': 'x001'} 10:00:19,B,{'id': 'x002'} 10:00:20,B,{'id': 'x002'} 10:00:22,A,{'id': 'x001'} 10:00:23,A,{'id': 'x001'} 10:00:24,B,{'id': 'x001'} 10:00:25,B,{'id': 'x002'} 10:00:26,B,{'id': 'x002'} 10:00:27,A,{'id': 'x001'} 10:00:28,A,{'id': 'x001'} 10:00:29,B,{'id': 'x001'} 10:00:30,B,{'id': 'x001'} 10:00:31,B,{'id': 'x001'} 10:00:32,A,{'id': 'x003'} 10:00:33,A,{'id': 'x003'} 10:00:35,B,{'id': 'x001'} 10:00:37,B,{'id': 'x001'} 10:00:39,B,{'id': 'x001'}

###試したこと・該当のソースコード
以前教えていただいたコードを改造して下記のように作りました

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.Second(5)))] 20 # 重複削除 21 use_m = matches.drop_duplicates(['material'])['material'] 22 print(use_m) 23 return None if matches.empty else use_m # <--ここがおかしい? 24 25df_mach['result'] = df_mach.apply(func, axis='columns') 26 27print(df_mach)

###発生している問題・エラーメッセージ
下記のようなメッセージが出ます.

ValueError: Wrong number of items passed 12, placement implies 1

さらに

python

1return None if matches.empty else use_m # <--ここがおかしい?

python

1return None if matches.empty else 1 # <--ここがおかしい?

とすると当然エラーは出なくなります。

お手数ですが、良い方法があればご教示願います。

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

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

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

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

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

guest

回答1

0

ベストアンサー

エラーの原因は DataFrame.apply() 内で指定した、func(row) の戻り値として Series型のデータを返していることかと思われます。

とりあえずであれば、func(row)の戻り値を

Python

1 return None if matches.empty else use_m # <--ここがおかしい?

から

Python

1 return None if matches.empty else use_m.tolist()

のように修正して、リスト型のデータを戻すようにすると良いのではないでしょうか。


【追加質問に関して】

質問のコードでは

Python

1use_m = matches.drop_duplicates(['material'])['material']

use_mに Pandas.Seriesデータが入っているのですが、
apply() のリターン値はそのままセルに格納されますので、リターンにて そのままの Seriesデータを返すとエラーとなってしまいます。(前回の質問)
そこで、Pandas.Series.tolist() にてSeriesデータをlist型のデータに変換することで問題を回避しております。(前回の解答)

しかしながら、実際のコードでは use_m にdict型のデータが格納されているようです(原因はわかりませんが・・)。
dict型のデータには、当然 list型にデータ変換を行う tolist() メソッドが存在しませんので、ここでエラーとなっております。

ただ、dict型のデータはlist型同様にそのままセルに格納することができますので、tolist()せずに、そのままリターンすると良いのではないでしょうか。


【更に追記です】

質問に挙げているソースコードですが、複数のデータ('material'データのリスト)を単一のセルに押し込めている部分がどうも気になります。
個人的には 'material'データ毎に行を分けて格納し、データ処理(取得)時にgroupby() でまとめた方が管理しやすいと思います。

ということで、簡単にサンプルを書きました。

Python

1from datetime import datetime, date,time,timedelta 2import pandas as pd 3 4my_parser = lambda x: pd.datetime.strptime(x, '%H:%M:%S').time() 5 6# machine.csv の読み込み('time'を'start_time'にRename / 'end_time'を追加) 7df_mach = pd.read_csv('machine.csv', date_parser=my_parser, parse_dates=['time']).rename(columns={'time':'start_time'}) 8df_mach['end_time'] = df_mach.apply(lambda d:datetime.combine(date.today(), d.start_time) + timedelta(seconds=5), axis=1).dt.time 9 10# material.csv の読み込み 11df_mate = pd.read_csv('material.csv', date_parser=my_parser, parse_dates=['time']) 12 13# machineデータとmaterialデータをマージ(時間範囲外は削除) 14df = df_mach.merge(df_mate, on='machine') 15df = df[(df.start_time<=df.time) & (df.time<=df.end_time)].reset_index(drop=True) 16print(df)

やっていることは machine.csvmaterial.csvをマージしただけです。
(重複削除はしておりません)

で、データ取得時に

Python

1grp = df.groupby(['start_time','machine']) 2 3# 全グループを取得 4print(grp.groups) 5 6# 10:00:20 開始のMachine-Bのデータを取得 7print(grp.get_group((time(10,0,10),'B'))) 8 9# 上記のデータの materialのリストを取得して重複削除 10print(grp.get_group((time(10,0,20),'B'))['material'].drop_duplicates()) 11 12# 前回の解答と同等のデータフレームを作成 13print(grp.apply(lambda d: d['material'].drop_duplicates().tolist())) 14 15# 全グループのデータをループ処理 16for key, data in grp: 17 print(key) 18 print(data['material'].drop_duplicates())

と様々な処理をする事ができます。

投稿2017/05/22 07:05

編集2017/05/24 07:08
magichan

総合スコア15898

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

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

pepasuke623

2017/05/25 14:23 編集

ありがとうございます! 確かにこれならできます。 実は私が提示したサンプルコードは本当はもっと長いです。そして、これは質問用に短く加工したものです。データファイルの形式もCSVではなくJsonファイルを読み込んでいます。多分そのせいなのか、読み込んだ際に辞書型(?)になってしまい、 "'dict' object has no attribute 'tolist'", 'occurred at index 121' とエラーメッセージが現れてしまいます。折角ご回答いただいたのに申し訳ございません。 もし、このようなエラーメッセージのご経験がございましたら、不躾ですが引き続きご教示願います。ございませんでしたら、この質問をクローズいたします。
magichan

2017/05/23 11:29

申し訳ありませんが、ちょっと時間が取れません。後日記入しますのですこしお待ちください。
magichan

2017/05/24 07:06

更新しました
pepasuke623

2017/05/25 14:31 編集

ありがとうございます! .mergeでくっつける方法を自分の該当コードで実行してみましたが、うまく行かなかったので引き続き見直してみます。 それより、「こういう書き方をすれば汎用性をもたせたり、後で自由に処理が出来る」ということで非常に勉強になりました。非常に丁寧な解説でわかりやすく感激いたしました! 最初に質問したあと、色々と試行錯誤していくなかで、dict型に変換されてしまうのはどうやらJsonファイルを読み込んだからのようです。これに関しても未解決なので別トピックとして質問させていただきます。 末筆ながら、重ね重ね感謝申し上げますm(_ _)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問