裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

リストは使わない,さらに,numpy.ndarray よりよい方法もある

2020年12月18日 | ブログラミング

なんでもかんでもリストで済ませようというのは止めよう。

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 

コメント (1)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

前の記事の展開(Python)

2020年12月18日 | Python

a = ("foo", "bar", "baz")

のとき,x が "foo" なら 5,"bar" なら 9,"baz" なら 17 を返すようにするなら(他の値はないとする)

(5, 9, 17)[("foo", "bar", "baz").index(x)]

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

式の右辺の if - else (Python)

2020年12月18日 | Python

Python だと

x = "odd" if a%2 == 1 else "even"

になるんだけど,以下の方がまだましだと思うのだ

x = ("odd", "even")[a%2 != 1]

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村