20160514

broken axisこと、にょろにょろ。

軸を省略するにょろにょろが描きたい。
broken axisで検索すれば出てきますよ。
broken axisことにょろにょろを、matplotlibで出してみる。

やばい、にょろにょろが目標だったが、間に合わないかもしれない。
Broken_axis_017.png
とりあえず、ズバズバの画像を載せておきます。
と、思ったが、間に合った@ 10:17 p.m.
Broken_axis_018.png
こういうのが目標。にょろにょろことサインカーブ。

1週間くらいは、前後の脈絡がない記事になっている。
Brokenaxisもうやったじゃんってなるのに、知らない体で書いてたりするかも。

そう、書き溜めているからです。
書き溜め〜見切り発車のダブルコンボ。

動機説明のためのサンプルコード↓。

import matplotlib.pyplot as plt
import numpy as np
import random

number = 20
randmin = 5
randmax = 30

label = []
x = np.zeros(number)
y = np.zeros(number)


for i in range(number):
v = random.randint(0,1)
if v == 0:
label.append("Mr. " + chr(i+65))#chr(65+0) is 'A', and chr(65+25) is 'Z'
else:
label.append("Ms. " + chr(i+65))#chr(65+0) is 'A', and chr(65+25) is 'Z'
#http://hetapy.hatenablog.com/entry/2013/08/23/115537
x[i] = i
y[i] = random.randint(randmin, randmax)


y[random.randint(0, number)] = random.randint(randmax+50, randmax+80)

max(y)

fig = plt.figure()
#ax = plt.subplot()
plt.bar(x, y, align='center')
plt.xticks(x, label, fontsize = 7)
plt.xlim(-1,number+1)

fig.tight_layout()
fig.set_size_inches(7.2,4.05)
fig.savefig("002.png", dpi=266, facecolor='w', edgecolor='w',
orientation='portrait', papertype=None, format=None,
transparent=False, bbox_inches=None, pad_inches=0.1,
frameon=None)

plt.show()


例えば、このくらいの差なら、まだいいが...
001.png
軸を省略したデータの中に、部分的にずば抜けてスケールが大きい、もしくはずば抜けて小さいデータが入っていたとき、同じスケールのグラフに描いてしまうと、まわりが目立たなくなってしまう場合。


y[random.randint(0, number)] = random.randint(randmax+900, randmax+1000)



頭一つどころじゃなく飛び抜けた成績。
004.png


y[18] = random.randint(randmin, randmax)
y[5] = random.randint(-2000, -1500)



その逆。
006.png

ずば抜けた超人達だけが勝負しているわけじゃない。下々の方々にも戦いがある。
その人達だけに焦点を合わせるとすると、

plt.xlim(0,30)


003.png
ただ、このやり方だと、飛び抜けちゃった人が、いかに飛び抜けているか分からない。

一つのやり方

plt.yscale("log")


007.png
log使っちゃうという…
極端に大きい場合も、極端に小さい場合も、ギュッと圧縮され、それなりに見える。
ただ、このやり方だと、0をまたいでマイナス側にいけない。

理由は、PyQtGraphでINFを出した記事とおなじだと思う。

subplotで、無理矢理解決する方法がある。
(マイナス側は、絶対値で出す。なんて難しい事をしなくても、下記の裏技"symlog"があるから平気。)
logでグラフを描き慣れている人は、思わず2度見する事間違いなし。
import matplotlib.pyplot as plt
import numpy as np
import random

number = 20
randmin = -25
randmax = 25

label = []
x = []
y = np.zeros(number)

for i in range(number):
v = random.randint(0,1)
if v == 0:
label.append("Mr. " + chr(i+65))#chr(65+0) is 'A', and chr(65+25) is 'Z'
else:
label.append("Ms. " + chr(i+65))#chr(65+0) is 'A', and chr(65+25) is 'Z'
x.append(i)
y[i] = random.randint(randmin, randmax)


y[18] = random.randint(randmin+1950, randmax+1975)
y[5] = random.randint(randmin-1975, randmax-1950)


fig = plt.figure()

plt.bar(x, y, align='center')

plt.xticks(x, label, fontsize = 7)
plt.xlim(-1,number+1)
plt.axhline(0, color = "k")

plt.yscale('symlog')

fig.tight_layout()
fig.set_size_inches(7.2,4.05)
fig.savefig("008.png", dpi=266, facecolor='w', edgecolor='w',
orientation='portrait', papertype=None, format=None,
transparent=False, bbox_inches=None, pad_inches=0.1,
frameon=None)

plt.show()

008.png
logスケールなのにマイナスまでいっているという所が二度見ポイント。

作成の際のポイントは、plt.xscale('symlog')
matplotlib.maか、リスト内包表記を使って、±のデータをデータを、仕分けするとか、そういうやり方もあってもいいと思う。

効率の悪さを追求したグラフ

symlogで一発でできることを、わざわざ自分で用意して描いてみる。
fig,(ax1, ax2) = plt.subplots(2, 1, sharex = True)

y1 = [v if v > 0 else 0 for v in y]
y2 = [v if v <= 0 else 0 for v in y]
ax1.bar(x,y1)
ax2.bar(x,y2)
plt.xticks(x, label, fontsize = 7)

plt.show()



010.png

とりあえず描いてしまったグラフの、最大値最小値が知りたい。
>>> ax1.get_ylim()
(0.0, 2000.0)
>>> ymin, ymax = ax1.get_ylim()
>>> print ymin
0.0
>>> print ymax
2000.0
>>> print(ax1.get_ylim())
(0.0, 2000.0)
>>>
らしい。

ついでに、ラベル、ticksも知りたい。
コレを使って、本来は描けない、負の数のlogスケールを描く。
>>> plt.xticks()
(array([ 0., 5., 10., 15., 20.]), )
>>> plt.yticks()
(array([ 0., 500., 1000., 1500., 2000.]),
)
>>> ax2.yticks()
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'AxesSubplot' object has no attribute 'yticks'

なんでだ!

>>> ax2.get_yticks()
array([ 0., 500., 1000., 1500., 2000.])

subplotの場合、get_いるみたい。

>>> -1*ax2.get_yticks()
array([ -0., -500., -1000., -1500., -2000.])
おっ、できちゃいますね。
numpy.arrayだからですかね。
まぁ、これはlogしたあとでね。

plt.yscale("log")
-1*ax2.get_yticks()
>>> plt.yscale("log")
>>> -1*ax2.get_yticks()
array([ -1.00000000e-01, -1.00000000e+00, -1.00000000e+01,
-1.00000000e+02, -1.00000000e+03, -1.00000000e+04,
-1.00000000e+05])

らしいので、
import matplotlib.pyplot as plt
import numpy as np
import random

number = 20
randmin = -25
randmax = 25

label = []
x = []
y = np.zeros(number)

for i in range(number):
v = random.randint(0,1)
if v == 0:
label.append("Mr. " + chr(i+65))#chr(65+0) is 'A', and chr(65+25) is 'Z'
else:
label.append("Ms. " + chr(i+65))#chr(65+0) is 'A', and chr(65+25) is 'Z'
x.append(i)
y[i] = random.randint(randmin, randmax)


y[18] = random.randint(randmin+1950, randmax+1975)
y[5] = random.randint(randmin-1975, randmax-1950)


fig,(ax1, ax2) = plt.subplots(2, 1, sharex = True)

y1 = np.array([v if v > 0 else 0 for v in y])
y2 = np.abs([v if v <= 0 else 0 for v in y])


ax1.bar(x,y1,align='center')
ax2.bar(x,y2,align='center')

plt.xticks(x, label, fontsize = 7)


ax1.set_yscale("log")
ax2.set_yscale("log")

ymin1, ymax1 = ax1.get_ylim()
ymin2, ymax2 = ax2.get_ylim()

plt.xlim(min(x)-0.5,max(x)+0.5)
plt.ylim(ymax2, ymin2)

label2 = ax2.get_yticks()
#ax2.set_yticks(ax2.get_yticks(),str(ax2.get_yticks()*-1))

#label2 = [str(-1*ax2.get_yticks()[i]) for i in range(len(ax2.get_yticks()))]

print label2
print ax2.get_yticks()

print ("label2 "+str(len(label2)))
print ("ax2.get_yticks() "+str(len(ax2.get_yticks())))


plt.yticks(ax2.get_yticks(),[str(-1*ax2.get_yticks()[i]) for i in range(len(ax2.get_yticks()))])


fig.tight_layout()
fig.set_size_inches(7.2,4.05)
fig.savefig("009.png", dpi=266, facecolor='w', edgecolor='w',
orientation='portrait', papertype=None, format=None,
transparent=False, bbox_inches=None, pad_inches=0.1,
frameon=None)

plt.show()

013.png
とかすれば、リスト内包表記を使って、無理矢理マイナス側のlogを描く方法。

これ以上体裁整えるのは止めておく。
ギブアップ。
どうしても-1000がみっともなくて替えたいときはlatexの出番でしょう。

LaTexは、ここでやってたので見てほしい。
2016年05月01日
matplotlib で、テキスト、数式というかLaTex。
014.png
先ほどのグラフとの違いが、お分かりいただけただろうか。
下側のグラフのyのticksの0に入れた。

そう、数字を入れたりするのが面倒なのだった...

まあ、LaTexできますね。
桁数を取得して、LaTexになるような文字列を、forか何か使って入れればいいんだよね。
やらないと決めたのでやりませんが。

logスケールで描く方法を二つ試した。
・ax.set_yscale("symlog")を使った正負両対数グラフ
・リスト内包表記を使って、±をわけて、absを使って、マイナスを取ったり工夫しながらsubplotで描き分けるグラフ。

ただ、マイナス側のログは、気持ち悪い。
ここまでやっといていつまでたっても、Broken axis出て来ねえなぁと思ったので、そろそろ本題に入る。

こういうとき、おなじみのコレを使う。


Broken axis。
いわゆる、にょろにょろ。

参考にしたページでは、にょろにょろじゃなく、//ズバズバ?だった。
http://matplotlib.org/examples/pylab_examples/broken_axis.html

MatPlotLibでは、broken axisと言うらしい。


もう面倒だ。コードだけ載せる。
"""
にょろにょろ
"""
import matplotlib.pyplot as plt
import numpy as np
import random

number = 20
randmin = -25
randmax = 25

label = []
x = []
y = np.zeros(number)

for i in range(number):
v = random.randint(0,1)
if v == 0:
label.append("Mr. " + chr(i+65))#chr(65+0) is 'A', and chr(65+25) is 'Z'
else:
label.append("Ms. " + chr(i+65))#chr(65+0) is 'A', and chr(65+25) is 'Z'
x.append(i)
y[i] = random.randint(randmin, randmax)

y[18] = random.randint(randmin+1950, randmax+1975)
y[5] = random.randint(randmin-1975, randmax-1950)


f, (ax1, ax2, ax3) = plt.subplots(3, 1, sharex=True)
ax1.spines['bottom'].set_visible(False)
ax2.spines['top'].set_visible(False)
ax2.spines['bottom'].set_visible(False)
ax3.spines['top'].set_visible(False)

ax1.xaxis.tick_top()
ax2.tick_params(axis='x', which='both', bottom='off', top='off', labelbottom='off')
ax3.xaxis.tick_bottom()
ax1.tick_params(labeltop='off')

ax1.bar(x, y, align='center')
ax2.bar(x, y, align='center')
ax3.bar(x, y, align='center')
plt.xticks(x, label, fontsize = 7)
ax1.set_ylim(1950, 2000)
ax2.set_ylim(-25,25)
ax3.set_ylim(-2000, -1950)

plt.subplots_adjust(wspace=0, hspace=0.05)

d = .015

x1 = np.linspace(-d, +d, 100)
x2 = np.linspace(1 - d, 1 + d, 100)
y1 = np.sin(np.linspace(0, 2*np.pi, 100))/50
y2 = np.sin(np.linspace(0, 2*np.pi, 100))/50+1

kwargs = dict(transform=ax1.transAxes, color='k', clip_on=False)
ax1.plot(x1, y1, **kwargs)
ax1.plot(x2, y1, **kwargs)

kwargs.update(transform=ax2.transAxes)
ax2.plot(x1, y2, **kwargs)
ax2.plot(x2, y2, **kwargs)

kwargs = dict(transform=ax2.transAxes, color='k', clip_on=False)
ax2.plot(x1, y1, **kwargs)
ax2.plot(x2, y1, **kwargs)

kwargs.update(transform=ax3.transAxes)
ax3.plot(x1, y2, **kwargs)
ax3.plot(x2, y2, **kwargs)

f.tight_layout()
f.set_size_inches(7.2,4.05)
f.savefig("018.png", dpi=266, facecolor='w', edgecolor='w',
orientation='portrait', papertype=None, format=None,
transparent=False, bbox_inches=None, pad_inches=0.1,
frameon=None)

plt.show()


で、
Broken_axis_018.png

ズバズバが、x、yが2点ずつのプロットなのに対して、これは100点もプロットしてしまった。
にょろにょろ。

やってみて、「おおっ!アレやコレやと今まで遠回りした甲斐あった。」と思った。
リスト内包表記とかやってなければ分からんもん。

数日分に分ければ良かった。途中で気づいていたけど、タイトル付け替えたり面倒くさかった。期を逃した。後の祭り。

「見る人のことなんか考えない。それが長続きの秘訣さ!」
と臆面もなく書けるほどの長続きを目指したいです。

あと1週間でちょうど2ヶ月。
3ヶ月続くと、googleの順位が劇的に上がるらしい。

日本語読める人>>>プログラムに興味がある人>>>pythonに興味がある人>Matplotlibに興味がある人。>>broken axisを調べる人、といった具合に人数が減っていく。

アフィリエイトを期待するならば、携帯電話の裏技みたいなのとか、アイドルとかの方がいいのかもしれない。
pythonじゃ無理だと書いていて気づく。

確かにpythonの求人って少ないよ。
でも、この前ドイツ人とかヨーロッパの人はなぜかpythonを好んで使うって、車関連のお仕事してる方に聞いたんだよ。表現がざっくりしててごめん。

遅いけど簡単。

要するに、、、
だれか右上のアドから楽天で業務用のチーズとか買ってよ...
IT系の人はどっかの人材派遣の登録とかしてくれ。頼んだぞよ。
(20160519)
明らかにsymlogが、一番見やすいグラフになっている。
posted by yuchan at 22:30 | Comment(1) | python