ファイルを読み込む¶
カンマ区切りのCSVファイルを読み込んで、データを処理してみます。 対象データとして、 株価データダウンロードサイト (k-db.com) から日経平均株価の日足を利用させて頂きます。
以下のデータ (csv-1.csv) は「日付,始値,高値,安値,終値」の並び順で、 6月第1週の日経平均株価です。
2014-06-06,15138.75,15144.34,15042.59,15077.24
2014-06-05,15112.59,15141.14,15016.81,15079.37
2014-06-04,15067.41,15071.78,14985.21,15067.96
2014-06-03,15089.04,15091.49,15026.01,15034.25
2014-06-02,14777.51,14963.91,14777.51,14935.92
列番号と配列インデックス、その内容は以下の表のようにまとめられます。
列番号 | 配列インデックス | 内容 | データ型 |
---|---|---|---|
1 | 0 | 日付 | 文字列/日付 |
2 | 1 | 始値 | 小数 |
3 | 2 | 高値 | 小数 |
4 | 3 | 安値 | 小数 |
5 | 4 | 終値 | 小数 |
自力で実装する方法¶
Python の基本的な部分から学習するために、組み込みの機能だけで実装してみます。 open() でファイルを開き、イテレータを使って CSV 形式 (カンマ区切りテキスト) のファイルを読み込みます。 with を使うことでリソースリークなどを予防できます。 文字列から小数に変換するには float() を使い、小数点以下N桁で丸めるには round() を使います。
スクリプトファイルの構成 で作成したスクリプトテンプレートから csv-1.py を作ってみましょう。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Parse daily Tokyo stock prices.
"""
import argparse
import logging
def parse_args():
"""Parse arguments and set up logging verbosity.
:rtype: parsed arguments as Namespace object.
"""
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--file", dest="filename",
help="setting file", metavar="FILE")
parser.add_argument("-o", "--output", dest="output",
help="output file", metavar="FILE")
parser.add_argument("-n", "--dryrun", dest="dryrun",
help="dry run", default=False, action="store_true")
parser.add_argument("-v", "--verbose", dest="verbose", default=False,
action="store_true", help="verbose mode")
parser.add_argument("-q", "--quiet", dest="quiet", default=False,
action="store_true", help="quiet mode")
# Add this line from boilerplate.
parser.add_argument("filename", nargs=1, help="CSV file path")
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
elif not args.quiet:
logging.basicConfig(level=logging.INFO)
return args
def process(args):
"""Parse daily Tokyo stock prices, and calculate up/down.
"""
with open(args.filename[0]) as fp:
for line in fp:
l = line.rstrip('\r\n')
t = l.split(',')
# Assign each field on individual variables.
day = t[0]
price_begin = float(t[1])
price_max = float(t[2])
price_min = float(t[3])
price_end = float(t[4])
# Calculate the differenciate of the day.
diff = price_end - price_begin
if diff > 0:
message = 'up'
elif diff < 0:
message = 'down'
else:
message = 'same'
# Write out day, up/down/same, and diff.
print('{}\t{:5}\t{}'.format(day, message, round(diff, 2)))
def main():
args = parse_args()
process(args)
def test():
pass
if __name__ == '__main__':
main()
# vim: set et ts=4 sw=4 cindent fileencoding=utf-8 :
実行結果
$ python csv-1.py csv-1.csv
2014-06-06 down -61.51
2014-06-05 down -33.22
2014-06-04 up 0.55
2014-06-03 down -54.79
2014-06-02 up 158.41
このスクリプトでは、入力データに意図しないカンマが混入した場合に想定外の出力となります。 たとえば、フランスでは小数点の区切り文字がカンマなので、数値をうまく分割できないでしょう。 このスクリプトを改善しても構いませんが、次からは標準モジュール csv を使っていきましょう。
そもそも CSV とは何か?¶
インターネットにおけるデータ交換方式、という観点からは RFC で規定されています。
- RFC 4180 (Common Format and MIME Type for Comma-Separated Values (CSV) Files)
しかし、一般に広く普及している形式は Microsoft Excel が書き出す形式であり、 RFC 4180 と等価ではありません。 記号のエスケープルールや、暗黙的な型変換に違いがあります。
Comma-Separated Values - ja.wikipedia.org: | |
---|---|
レコードにコンマやダブルクォートが含まれている場合、エスケープされている場合でも、ソフトによって解釈が異なり、区切り方が変わることがある。 その結果、データが破壊されることや、修正の手間が生じることがある。 レコードにタブ文字が含まれている場合よりも、レコードにコンマやダブルクォートが含まれている場合のほうが多い。 従って、CSVの代わりに、タブ区切りテキスト形式(TSV)を使うことで問題を避けられることがある。 |
また、数値をダブルクォートで囲まない場合、小数点の記号と混同される地域もあります。 たとえば、Windows のパソコンでロケールをフランス語にして Excel を使うと、カンマが小数点の区切り文字になります。
小数点 - ja.wikipedia.org: | |
---|---|
また非英語圏の国においては、コンマ (,) が小数点として用いられ、ピリオド (.) が3桁ごとの位取りに用いられる。 すなわち、日本と逆である。 |
したがって、「CSV でデータをください」という話題が出た場合には注意しましょう。 一見簡単そうですが、細かい部分に違いがあります。 可能ならば JSON (RFC 4627) のように構造化されてデータ型に制限のあるフォーマットを検討してください。 人間が読み書きするデータとコンピュータが読み書きするデータは、処理の難易度が必ずしも等価でないことに気をつけましょう。