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

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

ただいまの
回答率

90.50%

  • Python

    11784questions

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

pythonでログの分析(時間)が出来ません。

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 745

fuzball_yumi

score 7

前提・実現したいこと

pythonのプログラムで質問です。
以下のログがあります。

2012/01/02 13:00 0
2012/01/02 14:00 1
2012/01/02 14:01 1
2012/01/02 14:30 0
2012/01/02 14:31 0
2012/01/02 14:40 1
2012/01/03 01:00 0

このログの0と1の時間を日にちごとに集計を行いたいのです。
途中、0と1の間に1が入ったり、0が入ったりしたときは、無視をして、
(上のログの場合、14:00の1、14:31の0)です。
2012/01/02と2012/01/03で日にちをまたいだときは、23:59まで前日
00:00から2012/01/03に入れるようにしたいのですが方法が解りません。

プログラムのサンプルをお願いします。

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import csv
import datetime


#CSVファイルを読み込んで、各行毎のリストにする。
f = open("/usr/local/bin/log.csv")
reader= csv.reader(f, delimiter=",", quotechar='"')
records = list(reader)
f.close()

#2列目が0の行と1の行に分ける
from_list = [datetime.datetime.strptime(record[0], "%Y/%m/%d %H:%M:%S") for record in records if record[1]=='1']
to_list = [datetime.datetime.strptime(record[0], "%Y/%m/%d %H:%M:%S") for record in records if record[1]=='0']

#日毎の結果を格納する辞書を用意
result_dic = {}

#to - from を行い、間の経過時間を取得して日付をkeyにした辞書に加算していく。
for from_dt, to_dt in zip(from_list, to_list):
    if result_dic.get(str(from_dt.date())) is None:
        result_dic[str(from_dt.date())] = (to_dt - from_dt).total_seconds() / 60
    else:
        result_dic[str(from_dt.date())] += (to_dt - from_dt).total_seconds() / 60






#日付をkeyにした辞書のままだと日付で昇順に表示する時に面倒なのでリストに変換。
results = [{"date": date_str, "min": total_min} for date_str, total_min in result_dic.items()]




results.sort(key=lambda x:x['date']) #日付でソート



for result in results: #表示

    print("{0}, TOTAL{1}min".format(result['date'], int(result['min'])))

試したこと

0と1で分けて実行したのですが、0が連続したり1が連続したときにおかしくなります。
日付をまたぐと前日に入ってしまいます。

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

より詳細な情報

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

こんな感じでどうでしょうか?

pythonだと、日付でgroupbyできるので、それでまず日付で分けて、次に、1日の中で
集計しています。

また、その日の終わりを23:59としてしまうと、1分中途半端になってしまうので、
その日の最後の24:00までで計算してみました。

# -*- coding: utf-8 -*-

import csv
import datetime
from itertools import groupby

#CSVファイルを読み込んで、各行毎のリストにする。
f = open("log.csv")
reader= csv.reader(f, delimiter=",", quotechar='"')
records = list(reader)
f.close()

records = [ { "datetime": datetime.datetime.strptime(record[0],"%Y/%m/%d %H:%M") ,
              "state" : record[1] } for record in records ]


group = groupby( records, lambda record:record["datetime"].date() )

results_dict = {}
state = "0"

for day,daily_records in group:
    day_text = str(day)
    results_dict[day_text] = 0
    daily_records = sorted(daily_records,key=lambda record:record["datetime"])
    prev = datetime.datetime.combine(day,datetime.time(0,0))
    for record in daily_records:  # その日のレコードを計算
        now = record["datetime"]
        if state == "1":
            results_dict[day_text] += (now - prev).total_seconds() / 60
        prev = now
        state = record["state"]
    if state == "1":  # その日の最後までを計算
        next_day = day + datetime.timedelta(days=1)
        next_datetime = datetime.datetime.combine(next_day,datetime.time(0,0))
        results_dict[day_text] += (next_datetime - prev).total_seconds() / 60

results = [{"date": day_text, "min": total_min} for day_text , total_min in results_dict.items()]

results.sort(key=lambda x:x['date']) #日付でソート

for result in results: #表示

    print("{0}, TOTAL {1} min".format(result['date'], int(result['min'])))

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/08/25 17:16 編集

    上のプログラムを実行したらエラーが出ました。
    Traceback (most recent call last):
    File "ptime_3.py", line 13, in <module>
    records = [ { "datetime": datetime.datetime.strptime(record[0],"%Y/%m/%d %H:%M:%S") ,"state" : record[1] } for record in records ]
    IndexError: list index out of range

    これを試すのに使ったCSVファイルはこちらです。
    2017/08/24 13:38:11,0,
    2017/08/24 13:39:44,1,
    2017/08/24 14:09:03,0,
    2017/08/24 14:18:42,1,
    2017/08/24 14:50:40,0,
    2017/08/24 14:51:07,0,
    2017/08/24 14:53:05,1,
    2017/08/24 15:05:06,0,
    2017/08/24 15:11:17,0,
    2017/08/24 15:13:14,1,
    2017/08/24 15:31:32,0,
    2017/08/24 15:31:31,0,
    2017/08/24 15:31:31,0,

    お手数お掛けします。

    キャンセル

  • 2017/08/25 19:26

    csv.readerの引数を見直みてしてください

    質問されたときのcsvは区切りが空白で分単位、コードでは区切りがカンマで秒単位(集計は分単位)
    となっていたため、どちらに合わせるべきかわからないため、
    csvは区切りをカンマ、分単位のレコードと仮定しています。

    秒単位なら、集計ロジックも秒単位にした方が良いと思いますが

    キャンセル

  • 2017/08/26 08:26

    いろいろとありがとうございました。
    CSVを分に直したら出来ました。

    ご丁寧にありがとうございました。

    キャンセル

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

  • Python

    11784questions

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