これの少し続き。
「box プロットしたかった」と書いたが、要は前回描いたような「全部」じゃなくて統計的な「要約」が欲しい、というわけで、なので近い用が足せるなら box プロットそのものにこだわる必要はなかったりする。
一応 IQR を直接計算出来たりするけれど、やってみたら(やるまでもなくなのかもな)目的のと違うんだよね、これが。具体的には、「1~10の値を取り得るスコアの分布」というデータを持っている場合に IQR を求めようとしても、元が整数しかないなら整数しか返らない。(これは median も同じ。そらそーだ、て話だが、こういう基礎を結構忘れている、ワタシは。)
で、要するにワタシは「分布の形状の要約」が欲しいわけだから、よく考えたら「μ ± σ」の推移をプロットしちゃえばいいんじゃないかしら、と。これも基礎を忘れててすごく個人的に困ったが、「標準偏差の値」がσでいいんだっけか? とするとこのσは直感よりデカいんだけど…:
前回のおさらいをしておくと、元のデータはこんな具合:
1 '2017-12-20 17:18:01', [45, 30, 60, 167, 278, 510, 1236, 2110, 1957, 1615]
2 '2017-12-20 20:09:01', [46, 30, 60, 167, 278, 508, 1237, 2117, 1961, 1620]
3 '2017-12-21 02:35:34', [48, 30, 61, 169, 280, 511, 1237, 2130, 1964, 1629]
4 '2017-12-21 08:09:10', [48, 29, 61, 170, 278, 517, 1239, 2131, 1973, 1633]
5 ...(snip)...
6 '2017-12-30 10:58:17', [60, 39, 68, 191, 354, 735, 2227, 4926, 5559, 3782]
7 '2017-12-30 14:09:19', [60, 39, 68, 192, 354, 737, 2242, 4953, 5594, 3805]
8 '2017-12-30 20:09:12', [61, 38, 68, 193, 357, 748, 2252, 5008, 5666, 3844]
で、前回は contourf を使った:
1 def build_score_dist_boxplot(dba, name):
2 from datetime import datetime
3 import numpy as np
4 import matplotlib.pyplot as plt
5
6 fig, ax = plt.subplots()
7 x_f = []
8 x_d = []
9 z = []
10 first = None
11 for dtstr, dist in dba.query_score_dist_by_name(name):
12 dt = datetime.strptime(dtstr[:-3], "%Y-%m-%d %H:%M")
13 if first is None:
14 first = dt
15 x_f.append((dt - first).total_seconds())
16 x_d.append(dtstr[5:-3])
17 z.append([float(d) / max(dist) for d in dist])
18
19 X, Y = np.meshgrid(x_f, list(range(1, 11)))
20 pc = ax.contourf(X, Y, np.array(z).T, 15, cmap=plt.get_cmap("rainbow"))
21 ax.set_xticks(())
22 fig.tight_layout()
23 fig.savefig(name + ".png")
24 plt.close(fig)
dba.query_score_dist_by_name
が上に示したデータを返してくる。
で、さっきの絵は今度はこうした:
1 def build_score_dist_boxplot2(dba, name):
2 from datetime import datetime
3 import numpy as np
4 from scipy.stats import norm
5 import matplotlib.pyplot as plt
6 import itertools
7
8 fig, ax = plt.subplots()
9 x_f = []
10 y = {
11 "m1s": [],
12 "mean": [],
13 "p1s": []
14 }
15 first = None
16 for dtstr, dist in dba.query_score_dist_by_name(name):
17 dt = datetime.strptime(dtstr[:-3], "%Y-%m-%d %H:%M")
18 if first is None:
19 first = dt
20 x_f.append((dt - first).total_seconds())
21 a = list(itertools.chain.from_iterable([[i + 1] * d for i, d in enumerate(dist)]))
22 sigma = np.std(a)
23 mean = np.mean(a)
24 med = np.median(a)
25 y["m1s"].append(mean - sigma)
26 y["mean"].append(mean)
27 y["p1s"].append(mean + sigma)
28
29 ax.plot(x_f, y["m1s"], 'b--')
30 ax.plot(x_f, y["mean"], 'r-')
31 ax.plot(x_f, y["p1s"], 'b--')
32 ax.set_xticks(())
33 ax.set_ylim((1, 10))
34 ax.grid(True)
35 fig.tight_layout()
36 fig.savefig(name + "_2.png")
37 plt.close(fig)
ただ、さっきのグラフ、やっぱし「要約しすぎ」なのよね、「詳細も要約も両方欲しいのだわぃ」という気分になっちゃうのよね。なので二つのグラフを混ぜちゃおうかと:
1 def build_score_dist_boxplot(dba, name):
2 from datetime import datetime
3 import numpy as np
4 import matplotlib.pyplot as plt
5 import itertools
6
7 fig, ax = plt.subplots()
8 x_f = []
9 x_d = []
10 z = []
11 #
12 y = {
13 "m1s": [],
14 "mean": [],
15 "p1s": []
16 }
17 #
18 first = None
19 for dtstr, dist in dba.query_score_dist_by_name(name):
20 #print("%r, %r" % (dtstr, dist))
21 dt = datetime.strptime(dtstr[:-3], "%Y-%m-%d %H:%M")
22 if first is None:
23 first = dt
24 x_f.append((dt - first).total_seconds())
25 x_d.append(dtstr[5:-3])
26 z.append([float(d) / max(dist) for d in dist])
27 #
28 a = list(itertools.chain.from_iterable([[i + 1] * d for i, d in enumerate(dist)]))
29 sigma = np.std(a)
30 mean = np.mean(a)
31 med = np.median(a)
32 y["m1s"].append(mean - sigma)
33 y["mean"].append(mean)
34 y["p1s"].append(mean + sigma)
35 #
36 X, Y = np.meshgrid(x_f, list(range(1, 11)))
37 pc = ax.contourf(X, Y, np.array(z).T, 15, cmap=plt.get_cmap("rainbow"))
38 ax.plot(x_f, y["m1s"], 'b--')
39 ax.plot(x_f, y["mean"], 'w-')
40 ax.plot(x_f, y["p1s"], 'b--')
41 #ax.set_xticklabels(x_d, rotation=90)
42 ax.set_xticks(())
43 fig.tight_layout()
44 fig.savefig(name + ".png")
45 plt.close(fig)
ということをして、3つのグラフを描いてみた:
悪くないかな。
ただ、今相手にしてるデータたちって、ほぼ全てが「7 を中心に分布」する傾向が強くて(つまりほとんどが上に偏る)、+σがどうしてもおかしな感じになるんだよねぇ。もう少し工夫が必要な気がしますわな。