裏 RjpWiki

文字通り,RjpWiki の裏を行きます
R プログラム コンピュータ・サイエンス 統計学

リストは使わない,さらに,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)   この記事についてブログを書く
« 前の記事の展開(Python) | トップ | 最初から,重箱の隅をつつか... »
最新の画像もっと見る

1 コメント

コメント日が  古い順  |   新しい順
AWK プログラム (r-de-r)
2020-12-18 18:36:51
BEGIN {
getline a
n = split(a, arry, " ")
for (i = 1; i <= n; i++) {
freq[arry[i]] += 1
}
}

END {
for (a in freq) {
printf "'%s' %d\n", a, freq[a]
}
}

結果
$ time awk -f freq2.awk char.dat|sort
'A' 384835
'B' 385558
'C' 383821
'D' 384050
'E' 384445
'F' 385576
'G' 384354
'H' 384822
'I' 385866
'J' 385421
'K' 385149
'L' 383431
'M' 384126
'N' 384411
'O' 384677
'P' 384022
'Q' 383844
'R' 385406
'S' 384972
'T' 384658
'U' 385097
'V' 383946
'W' 384470
'X' 383768
'Y' 384658
'Z' 384617

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。

ブログラミング」カテゴリの最新記事