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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

1286閲覧

Python 関数の利用

north_redwings

総合スコア32

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

1クリップ

投稿2018/02/20 03:18

csvファイルを読み込み、6,7月の売上を表示する事が課題です。
Errorは出ませんが、7月が0円でないのに(870円)、0円と出てしまいます。
試しに、6月の部分を消したところ、正常に作動しましたので関数を呼び出す部分周辺が怪しそうなのですが...。

ちなみに、csvファイルには各行にカンマ(,)区切りで 日付(yyyy-mm-dd hh:mm:ss)、 品目、 売り上げ金額 の順に入力されています。
ex)2014-07-14 13:36:59,オレンジジュース,110

また、プログラミング初心者なので、正誤問わず書き方に指摘や改善点等ございましたら、その点もご教授願います。

Python

1"""月ごとの売上金額の集計.""" 2import csv 3from datetime import datetime 4 5ENCODING = 'utf-8' 6KEYS_LIST = ['date', 'name', 'price'] 7 8 9def count(dict_list, month): 10 """ 指定した month の name, price をカウントしてcount_name, count_priceを返す 11 """ 12 count_name = {} 13 count_price = 0 14 for a_dict in dict_list: 15 date = datetime.strptime(a_dict['date'], '%Y-%m-%d %H:%M:%S') 16 if date.month == month: 17 name = a_dict['name'] 18 price = int(a_dict['price']) 19 count_price += price 20 print(count_price) 21 if name in count_name.keys(): 22 count_name[name] += 1 23 else: 24 count_name[name] = 1 25 return count_name, count_price 26 27 28def main(): 29 with open('input/report.csv', encoding=ENCODING) as file: 30 dict_list = csv.DictReader(file, fieldnames=KEYS_LIST) 31 count_name_6, count_price_6 = count(dict_list, 6) 32 print('6月: {}'.format(count_price_6)) 33 count_name_7, count_price_7 = count(dict_list, 7) 34 print('7月: {}'.format(count_price_7)) 35 36if __name__ == '__main__': 37 main() 38

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

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

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

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

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

guest

回答2

0

ベストアンサー

一度読み出すと、中身が空になってしまうdict_listを使っているせいかと思います。
リストに一度収めてから、以下のように変更してみてはいかがでしょう。

python

1 with open('input/report.csv', encoding=ENCODING) as file: 2 dict_list = csv.DictReader(file, fieldnames=KEYS_LIST) 3 dict_list = list(dict_list) 4 count_name_6, count_price_6 = count(dict_list, 6) 5 print('6月: {}'.format(count_price_6)) 6 count_name_7, count_price_7 = count(dict_list, 7) 7 print('7月: {}'.format(count_price_7))

投稿2018/02/20 03:48

編集2018/02/20 03:49
mkgrei

総合スコア8560

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

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

north_redwings

2018/02/20 04:32

勉強不足で申し訳ないのですが、どうして中身が空になってしまうのでしょうか... pythonのfor文はシーケンスは破壊(?)されてしまうという事でしょうか?
mkgrei

2018/02/20 04:49

破壊というよりも、読み出しの時にイテレータの類のオブジェクトが返ってきていて、 これは要するに命令されたら次の要素を返すものになっています。 それで、全部の要素を取り出しきってしまうと中身がなくなってしまいます。 それ以降何度次のものを取り出そうとしても空なので何も返ってきません。 もう一度使いたい場合は再度オブジェクトを作る必要があります。 つまり、for文に問題あるのではなく、csv.DictReaderの方にくせがあるのです。 今回の場合、一度メモリ上に読み込んでから、それを使い続ければ良いので、最初にリスト化しています。 リストにすると、for文などに出会うたびにイテレータを新たに生成して要素を逐一取り出しています。 イテレータみたいなものがある利点のひとつとしては、一度にすべてのデータをメモリ上に載せる必要が無いことなどが挙げられます。 例えば、今回のファイルが非常に大きくメモリに乗らない場合、一行ずつ取り出すことでメモリ使用量を抑えることができます。 おそらくこのようにイテレータ等が返り値になったのはPython3.xからで、必要に応じてlist()、dict()などで囲むことによって従来の使用感が得られます。
mkgrei

2018/02/20 04:53

ついでにcsv.DictReaderが元凶ではなく、元をたどると、fileに行き着くのですが、興味があるのであれば、イテレータ・ジェネレータを理解した上で、ソースコードを直接読むとすぐにわかります。
north_redwings

2018/02/20 05:09

なるほど...つまり、csv.dictreaderは厳密にはlistが返って来るわけではないのでlistとして用いたければlist()でキャスティングする必要がある、という解釈で合っていますでしょうか?
mkgrei

2018/02/20 05:11

おっしゃるとおりです。
north_redwings

2018/02/20 05:20

イテレータあたりをもっと勉強する必要がありそうです。 詳しく親切に回答していただき、ありがとうございました。
guest

0

csvファイルを扱う際にはpandasを使うことをおススメします。
また、dictをカウンターとして使う場合、defaultdictという便利なものがあります。

上記のものを使ったコードを書いてみましたので、興味があればご参考にしてください。

python

1from collections import defaultdict 2 3import pandas as pd 4 5ENCODING = 'utf-8' 6KEYS_LIST = ['date', 'name', 'price'] 7 8def count(df, month): 9 # monthに該当する行を探す 10 mask = (df["date"].map(lambda x: x.month) == month) 11 12 count_name = defaultdict(int) 13 count_price = 0 14 for name, items in df[mask].groupby("name"): 15 count_name[name] = items.count() 16 count_price += items["price"].sum() 17 18 return count_name, count_price 19 20def main(): 21 df = pd.read_csv("input/report.csv", names=KEYS_LIST, encoding=ENCODING) 22 # 文字列をdatetimeオブジェクトに変換する 23 df["date"] = pd.to_datetime(df["date"], format="%Y-%m-%d %H:%M:%S") 24 25 count_name_6, count_price_6 = count(df, 6) 26 print('6月: {}'.format(count_price_6)) 27 count_name_7, count_price_7 = count(df, 7) 28 print('7月: {}'.format(count_price_7)) 29 30if __name__ == '__main__': 31 main()

投稿2018/02/20 04:18

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

north_redwings

2018/02/20 04:33

どうもありがとうございます! pandasはほぼ手をつけられていませんが、後に勉強予定ですので勉強したら参考にさせていただきます。
R.Shigemori

2018/02/20 06:29

細かいことですが、to_datetimeを使うのであれば、そのままindexを置換したほうがresampleが使えて、月別集計が簡単になるように思いました。
退会済みユーザー

退会済みユーザー

2018/02/20 06:38 編集

コメントありがとうございます。お恥ずかしながらresampleを知りませんでしたm(_ _;)m とても使いやすそうなので後で修正してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問