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

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

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

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

pandas

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

Q&A

解決済

3回答

823閲覧

カッコ()で区切られたデータを連結したりして、正解と一致している区間を返す

pepasuke623

総合スコア55

Python

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

pandas

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

0グッド

0クリップ

投稿2019/06/13 04:02

編集2019/06/14 01:45

いつもお世話になっております。
Pythonのプログラミングでわからないところがあるので質問させていただきます。

前提

こんな実験をして、こんな状況があるとします

  • ある照明器具があって、これが所望の時間帯に発行しているかを評価したい
  • 照明器具はある計算規則によって発光する
  • 発光を判断するセンサがあって、ある閾値を超えた輝度に関しては発光しているものと判断して、閾値以下のものを発光していないと判定してくれる

そしてこの実験から下記のCSVファイルが得られたします。

//data.csv lamp,correct_time [(123 200) (200 400) (400 567)],[(268 574)] [(111 151) (412 567)],[(301 556)] [(168 199) (259 392)],[(174 580)]

のCSVファイルの詳細は次のとおりです

  • 列名「lamp」というのは発光を判断するセンサが発光していると判断している時間帯を計測している。例えば左上の(123 200)は計測開始から123秒後から200秒後まで発光していると判断している
  • このセンサは少し厄介なことがあって、計測開始から200秒毎に勝手に区切られてしまう。例えば一番上の行の(123 200) (200 400) (400 567)というのは実際には計測開始から123秒後から567秒後まで発光しているが200秒毎に区切られてしまって「123秒から200秒、200秒から400秒、400秒から567秒まで」というような表示になっている。
  • 列名「correct_time」というのは、実際に光っていてほしい時間帯、すなわち正解の時間帯を示している。一番上の行の(268 574)は計測開始から268秒後から574秒後まで光っていてほしいという意味である

実現したいこと

やりたいことしては次のとおりです

  • 列名「lamp」と「correct_time」がどれだけ一致しているかを返したいです。例えば、CSVの一番上の行であれば計測開始から268秒後から574秒後までが一致しているので、リストなどで[268 574]。
  • 上から2番めであれば412秒後から556秒後までが一致しているので[412 556]
  • 上から3番めであれば174秒後から199秒後と259秒後から392秒後までが一致しているので[174 199][259 392]

と返したいです。

試したこと

どのようにコードを書いたら良いのか分からず、全く試せていません・・
一応Pandasを使ってちょこっと書いてみましたが、ハテナです・・・
出来ればPandasを使って解決したいですが、その限りではございません。

全体的に分かりづらいかもしれませんが、是非ともご指南お願いいたします。

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

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

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

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

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

guest

回答3

0

ベストアンサー

タスクとしては「文字列 "[(123 200) (200 400) (400 567)]" を扱える形のオブジェクトに変換すること」と「区間の共通部分を計算すること」の2つにわかれるかと思います。

前者は Python の文法に合うようにカンマを入れてあげて、組み込み関数 eval() でタプルのリストに変換するといいかと思います。

後者は自分で実装しようとすると少々面倒なので、ライブラリを使うといいと思います。
sympySets で区間演算が行えるので、それを使うと楽です。

Sets — SymPy 1.4 documentation

手順

    1. pandas の read_csv() で csv ファイルを読み込む。
    1. 文字列をタプルのリストに変換する。
"[(123 200) (200 400) (400 567)]" -> [(123,200),(200,400),(400,567)]
    1. タプルのリストを sympy.Interval のリストに変換する。
    1. 列 lamp と列 correct_time の共通する区間のリストを計算する。(集合の積)
    1. sympy.Interval のリストを文字列に戻す。
[Interval.Ropen(111, 151), Interval.Ropen(412, 567)] -> "[(111 151) (412 567)]"
  1. 結果を csv に出力する。

サンプルコード

python

1import itertools 2 3import pandas as pd 4import sympy as sy 5 6 7def str_to_intervals(x): 8 """文字列を sympy.Interval のリストに変換する。 9 """ 10 # 空白をカンマに置換することで、python のリストのタプルとして、式を評価できるようにする。 11 # 例: "[(123 200) (200 400) (400 567)]" -> [(123,200),(200,400),(400,567)] 12 list_of_tuples = x.replace(" ", ",") 13 # 文字列を sympy.Interval のリストに変換する。 14 intervals = [sy.Interval(l, r, right_open=True) for l, r in eval(list_of_tuples)] 15 16 return intervals 17 18 19def intervals_to_str(intervals): 20 """sympy.Interval を文字列に戻す。 21 例: [Interval.Ropen(111, 151), Interval.Ropen(412, 567)] -> "[(111 151) (412 567)]" 22 """ 23 str_intervals = [f"({i.left} {i.right})" for i in intervals] 24 return "[" + " ".join(str_intervals) + "]" 25 26 27def calc_overlap(row): 28 """列 lamp と列 correct_time の共通する区間のリストを返す。 29 """ 30 # 列 lamp の区間と列 correct_time の区間のすべての組み合わせにおいて、 31 # 共通部分を計算し、その和集合をとる。 32 overlaps = sy.EmptySet() 33 for i1, i2 in itertools.product(row["lamp"], row["correct_time"]): 34 overlaps += i1 & i2 35 # Union の場合は、Interval のリストとして返す。 36 # 区間が1つの場合でも Interval のリストとして返す。 37 return overlaps.args if isinstance(overlaps, sy.Union) else [overlaps] 38 39 40df = pd.read_csv( 41 "test.csv", converters={"lamp": str_to_intervals, "correct_time": str_to_intervals} 42) 43# 共通部分を計算して、列 overlap に追加する。 44df["overlap"] = df.apply(calc_overlap, axis="columns") 45# sympy.Interval を文字列表現に戻す。 46df = df.applymap(intervals_to_str) 47# csv ファイルとして保存する。 48df.to_csv("reuslt.csv")

入出力

入力

csv

1lamp,correct_time 2[(123 200) (200 400) (400 567)],[(268 574)] 3[(111 151) (412 567)],[(301 556)] 4[(168 199) (259 392)],[(174 580)]

出力

csv

1,lamp,correct_time,overlap 20,[(123 200) (200 400) (400 567)],[(268 574)],[(268 567)] 31,[(111 151) (412 567)],[(301 556)],[(412 556)] 42,[(168 199) (259 392)],[(174 580)],[(174 199) (259 392)]

投稿2019/06/13 05:55

編集2019/06/13 06:08
tiitoi

総合スコア21956

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

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

pepasuke623

2019/06/14 01:43

ご丁寧な回答、感謝いたします。サンプルコードも添付していただいて非常に分かりやすいです。 走らせてみたところ ("'str' object has no attribute 'left'", 'occurred at index filename') とエラーメッセージが出て色々ググってみましたが分からず・・・。初歩的なところを見落としているのかもしれませんが、よろしければアドバイス願います。
tiitoi

2019/06/14 02:58

回答の「入力」の csv を用意すると、実行すると「出力」の csv のコードが出てくるというもので、こちらの環境では動作しています。 もし、入力の csv の形式が回答で想定しているものと異なる場合や質問者さんがコードを一部編集した場合は質問欄に追記していただけますか。
pepasuke623

2019/06/14 03:42

ご返信ありがとうございます。 今回投稿させていただいた内容は質問用にデフォルメしてあります。 列名などは違うものの、CSVファイル内の該当部分のフォーマットは同じであるはずです。頂いたサンプルコードの列名などを変えていますが、それ以外は変更していないと思います。(只今出払っていて、エラーになった方のCSVは見れませんので、後日もう一度確認します) また前回の私の説明が言葉足らずでしたが、 str_intervals = [f"({i.left} {i.right})" for i in intervals] の行で ("'str' object has no attribute 'left'", 'occurred at index filename') というエラーが出ていました。 後ほどもう一回見てみます。
tiitoi

2019/06/14 03:54

エラーの直接的な原因は intervals は sympy.Interval のリストのはずなのですが、文字列 str が混ざってしまっているみたいです。 なぜそうなったのかはわからないのですが、元の読み込むデータに原因があるのではと推測しております。 お時間が取れるときでよろしいので、df = pd.read_csv() した直後の DataFrame を確認して、おかしなことになっていないか確認してみてください。 可能であれば、実際の csv ファイルの一部 (最初の10行ぐらい) を質問欄に記載いただくか、csv ファイルをどこかのアップローダーにアップしていただければこちらでも検証できます。
pepasuke623

2019/06/14 04:13

コメントありがとうございます。解決しました。 おっしゃる通り、エラーが出た方のCSVは他に日付やその他の情報が入った列が存在するためにエラーになっていました。初歩的なところで、引っかかっていました。
guest

0

以下のような方法ではどうでしょう。

https://www.gesource.jp/weblog/?p=8244を参考に、「正解の時間帯」の区間を定義して、ある時点が「正解の時間帯」に含まれているか否かを判別する関数を作る。

「実際に光っている時間帯」の開始時点と終了時点をそれぞれ上記関数で判別する。

「実際の開始時点=正解の範囲内」かつ「実際の終了時点=正解の範囲内」なら(実際の開始時点、実際の終了時点)を一致した区間として返す。
「実際の開始時点=正解の範囲内」かつ「実際の終了時点=正解の範囲外」なら(実際の開始時点、正解の終了時点)を一致した区間として返す。
以下同様に。

投稿2019/06/13 04:36

KojiDoi

総合スコア13671

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

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

pepasuke623

2019/06/13 05:06

回答ありがとうございます!! なるほど、是非とも参考にさせていただきます。 ただ、私はどちらかというとカッコ入の文字列処理に苦戦しております。 力技でなんとか出来なくもないと思いますが、もっとスマートに出来ないかな、と思い質問させていただいた次第です。
KojiDoi

2019/06/13 05:27

正規表現で(開始、終了)というパターンをキャプチャすればいいのではないでしょうか。
pepasuke623

2019/06/14 01:40

回答有り難うございます。そうですね、正規表現は殆ど使ったことがないので、調べながら書いてみます。
guest

0

データを違うフォーマットで吐けるのなら、それが一番スマートなんですが・・・(JSONにでもしてくれればいいのに)

正規表現使ってやるしかなさそうですね。とにかくpythonオブジェクトにしちゃえば、あとは力技でなんとかなるでしょう。

python

1>>> import re 2>>> s = "[(123 200) (200 400) (400 567)]" 3>>> [list(map(int, data)) for data in re.findall(r"((\d+) (\d+))", s)] 4[[123, 200], [200, 400], [400, 567]]

あるいは、「そのフォーマットを100%信じていいのなら」空白をカンマに置換してあげるとpythonの式として読める文字列になりそうなので、それで取り扱うか。

python

1>>> from ast import literal_eval 2>>> s = "[(123 200) (200 400) (400 567)]" 3>>> literal_eval(s.replace(" ", ", ")) 4[(123, 200), (200, 400), (400, 567)] 5

投稿2019/06/13 05:33

hayataka2049

総合スコア30933

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

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

pepasuke623

2019/06/14 01:41

回答有り難うございます。勉強になりました。正規表現を殆ど使ったことがないので、調べてみます。このテクニックは他の問題にも適用できそうですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問