なんでもかんでもリストで済ませようというのは止めよう。
numpy.ndarray を使うともっといいよ。
ということだが,場合によってはそれよりいいものもあるよという話。
いずれにせよ,過度にリストに頼るのは止めようよという話
その 1
1000万文字の大文字アルファベットが空白で区切られて 1 行に入力されている char.dat というファイルを対象にする。
C P U H I J R S W Q H W R ....
「それぞれのアルファベットが何個あるか集計せよ」というタスクを考えよう。
from time import time
以下のようにデータを入力する
f = open("char.dat")
a = f.read()
f.close()
b = a.split()
# ここまでは共通
まず,リストと辞書を使って書いてみる
start = time()
c = sorted(set(b))
tbl = {}
for i in c:
tbl.setdefault(i, b.count(i))
for i, j in zip(tbl.keys(), tbl.values()):
print(i, j)
print(time() - start) # 5.874 sec.
所要時間は 5.874 秒。簡単なものだ。
(ちなみに,コメントに示す awk プログラムでは 3.846 秒だから)
リストだけを使って書くと 16.096 秒 となり,かなり遅い。
start = time()
c = sorted(set(b))
count = []
for i in c:
count.append(b.count(i))
for i, j in zip(c, count):
print(i, j)
print(time() - start) # 16.096sec.
次は numpy.ndarray を使って書いてみる。
numpy を最初に import するのに必要な時間を除いて実行時間を計測する。
import numpy as np
start = time()
value, count = np.unique(b, return_counts=True)
for i, j in zip(value, count):
print(i, j)
print(time() - start) # 1.686 sec.
所要時間は 1.686 秒。辞書型を使うよりは速い。
しかも,集計部分は
value, count = np.unique(b, return_counts=True)
の一行だ。
その 2
もう少し大きい数値データで見てみよう。
1500 変数,100000 行の CSV ファイルで,1行目は変数名である。
2行目以降は平均値 50,標準偏差 10 の正規乱数が小数点以下 2 桁で記録されている。
今回は,変数の区別なく, np.floor() で整数化し,総計 1500 x 100000 個の変数の度数分布を見る。
ファイルからデータを読み,np.floor でリストにする
import re
start = time()
f = open("test.csv")
a = f.read()
f.close()
b = re.sub(r'[",\n]', ' ', a)
x = list(map(np.floor, map(float, b.split()[1500:]))) # list
print(time() - start) # 471.080 sec.
入力だけでも 471.080 秒もかかるのだけど,さらに...
辞書型を使って集計--とんでもなく遅い 624.511 秒
start = time()
c = sorted(set(x))
tbl = {}
for i in c:
tbl.setdefault(i, x.count(i))
print(time() - start) # 624.511 sec.
# (dict_keys([-7.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0,
# dict_values([1, 2, 3, 6, 10, 21, 35,
np.unique() を使って集計--かなり速い 27.528 秒
start = time()
value, count = np.unique(x, return_counts=True)
print(time() - start) # 27.528 sec.
# (array([ -7., -5., -4., -3., -2., -1., 0.,
# array([ 1, 2, 3, 6, 10, 21, 35,
pandas でデータを入力し,np.ravel(), np.floor() で整数化して numpy.ndarray とする
import pandas as pd
start = time()
df = pd.read_csv('test.csv')
y = np.floor(np.ravel(df)) # numpy.ndarray
print(time() - start) # 33.201 sec.
start = time()
value, count = np.unique(y, return_counts=True)
print(time() - start) # 7.583 sec.
# (array([ -7., -5., -4., -3., -2., -1., 0.,
# array([ 1, 2, 3, 6, 10, 21, 35,
pandas の value_counts() を使うと,爆速 2.121 秒 一番速い
start = time()
ans = pd.value_counts(y)
ans2 = ans.sort_index()
print(time() - start) # 2.121 sec.
# -7.0 1
# -5.0 2
# -4.0 3
# -3.0 6
# -2.0 10
# ..
# 106.0 1
なお,R だと...遅い
> system.time({
+ df = read.csv("test.csv", colClasses="double")
+ x = as.integer(data.matrix(df))
+ tbl = table(x)
+ })
ユーザ システム 経過
50.547 5.324 55.960