20180305

matplotlibのmake_axes_locatableで作ったcolorbarの透明度を変更する

color bar の軸の色を調節する。

matplotlibのcolorbarをいじっている。

03.gif

colorbarの高さを、軸の高さに合わせるために、make_axes_locatableを使った。
make_axes_locatableを使った後、標準のcolorbarとは、使い勝手が変わっているように感じる。
とりあえず、make_axes_locatableを使った後の、cbarの軸を透明化する方法を、書き残しておく。


# coding: utf-8
import math
import time
from datetime import datetime as dt
import numpy as np
from PIL import Image
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib import ticker

N=16
img1 = [[math.sin(2 * np.pi * i * j /float(N)) for i in range(N)] for j in range(N)]


fig = plt.figure()
loc_ax = [0.1,0.2,0.8,0.7]
ax1 = fig.add_axes(loc_ax)
imgcol1 = ax1.imshow(img1, vmax=1, vmin=-1, cmap = 'gray', interpolation='nearest')
ax1.set_ylabel("frq m [Hz]")
ax1.set_xlabel("horizontal px h [px]")

divider1 = make_axes_locatable(ax1)
ax_cb0 = divider1.new_horizontal(size="10%", pad=0.05)
ax_cb01 = fig.add_axes(ax_cb0)
cb1 = plt.colorbar(imgcol1, cax=ax_cb0)
tick_locator1 = ticker.MaxNLocator(nbins=5)
cb1.locator = tick_locator1
cb1.update_ticks()
ax1.locator_params(axis='x', nbins=4)
ax1.locator_params(axis='y', nbins=7)
cb1.set_label("real part")
ax1.set_axis_on()

#############
#insert here#
#############

tdatetime = dt.now()
itext = tdatetime.strftime('%Y%m%d'+ time.ctime()[11:13] + time.ctime()[14:16] + time.ctime()[17:19])
fig.canvas.draw()
# save it to a numpy array.
data = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
data = data.reshape(fig.canvas.get_width_height()[::-1] + (3,))
im = Image.fromarray(data)
im.save("%s.png"%(itext))
plt.close()
im.close()

20180304144407.png

これから書くコードの断片は、上記コードの、

#############
#insert here#
#############

って所に、挿入するという形で進める。

禁止

  • 色付き画像を重ねる方法は禁止。
画像の上に、色調節したポリゴンか何かを重ねて、透明っぽくするのは禁止。
たとえば、imshowを使って、背景色と同じ色を重ねて、透明っぽくするとかは禁止。
あくまでも、各パーツを透明にする。
#############
#insert here#
#############
img2 = [[[191,191,191,50] for i in range(5)] for j in range(5)]
loc_ax2 = [
0.05,0.1,0.9,0.8]
ax2 = fig.add_axes(loc_ax2)
ax2.patch.set_facecolor([
0,0,0,0])#何も書いてない場合、背景は透明(ディフォルトでは白)。
imgcol2 = ax2.imshow(img2, vmax=1, vmin=-1, cmap = 'gray', interpolation='nearest')
こうやって、透明っぽくす誤魔化すのは無し。
(軸はあえて隠さなかった。)
20180304144936.png
アニメーション時の汎用性が下がる。
  • set_visibleで消すのも禁止。
「徐々に出現する」とか、「徐々に色が変わる」とかのアニメーションがしたい。
だから、True, Falseで一発で点いたり消えたりしてしまうのは無し。

各パーツ ax, spines, ticks(major, minor)

自分の語彙が無いせいだが、「軸」といったら、黒いところ全体を指して言っている。
matplotlibのドキュメントを見ると、ax, spines, ticks(major, minor)とか書き分けてある。
これ全部を透明にしたりして、出現するアニメーションをしたい。

20180304144407.png

colorbarのaxのspinesの色を変える。

20180304150030.png

色の指定は[R,G,B,A]。

color1=[0,0,0,0.0]
cb1.outline.set_edgecolor(color1)

20180304145512.png

透明にしたが、color1の部分を

color1=[1,0,0,0.8]

とかにすれば、当然着色もできる。

ticks 目盛りの部分。

20180304150707.png

ticksの部分は、

color1=[0,0,0,0.0]
cb1.ax.tick_params(axis='both', colors=color1)

label ラベルも消す。

20180304150959.png

カラーバーの場合、普通のaxと違って、余計に、axと入れないとダメ。

color1=[0,0,0,0.0]
cb1.ax.yaxis.label.set_color(color1)

二段階必要な、カラーバーの着色部分

20180304151635.png

色の部分を消したいだけなら下記でできるが、白いのが残る。カラーバーを書いてあるaxも、背景は白らしい。
alphaは、0.0〜1.0までの数値で選択。
色の指定の仕方[R,G,B,A]とは違うので注意。

cb1.solids.set_alpha(0)

20180304152157.png

背景の白いのは、下記コードで透明にできます。

cb1.patch.set_alpha(0)#layer under cbar.

カラーバーじゃない方の、axを透明化。

これらは調べればすぐにわかるけど…

spinesを消す。

20180304155248.png
color1=[0,0,0,0.0]
ax1.spines['bottom'].set_color(color1)
ax1.spines['top'].set_color(color1)
ax1.spines['left'].set_color(color1)
ax1.spines['right'].set_color(color1)

軸を消す。ticklabels(数字)も消える。

20180304155500.png
color1=[0,0,0,0.0]
ax1.tick_params(axis='both', colors=color1)

labelも消す。

20180304155619.png

x側は、

color1=[0,0,0,0.0]
ax1.xaxis.label.set_color(color1)

20180304155753.png

y側は、

color1=[0,0,0,0.0]
ax1.yaxis.label.set_color(color1)

imshowで描いたimageを消す

20180304155930.png
imgcol1.set_alpha(0.0)

20180304160002.png
ax1.patch.set_alpha(0.0)
posted by yuchan at 07:00 | Comment(0) | python

20180213

matplotlib floatingaxes と Affine2D で、axもimgも回転させ、colorも変えた。大変だった。

今回の目的

とりあえず、作ったものを載せる。

20180212211231.png

特徴は、

  1. 軸(subplot、axes)が回転している。
  2. 回転した軸の中身に、imshow(画像配列データの表示)を入れている。
  3. axの色、labelの色、tickの色、ticksの色、spinesの色、ticklabelsの色を、任意の色に変えている。

たったこれだけなのに、いつも通りにいかなかった。
いつものというのは、色の変更とか、軸の上限値とかをいじるコマンドのこと。
回転させると、いつものコマンドが効かなくなってしまう。
特に、3の、spines、ticklabelsの色を変える所が苦労した。
三連休つぶれた。

これをやろうと思った理由

動画で使うため。画像を回転させたくなった。
ここでやっている様に、行列の掛け算を、右側の行列をひっくり返して、上から移動させていくのをやりたい。
上記リンクの、画像バージョンを、次の動画の、1シーンでやろうとしている。
ここでは載せないが、その内、YouTubeにアップする。
とりあえず、回転だけさせたgifアニメは作った。

abcd.gif

floating_axesとは。

軸を回転させたかったので調べたら、この機能にたどり着いた。
このページの左端のサブプロットがやりたい。floating_axesで、軸を回転させることができることを知った。floating_axesが何をやっているのかは知らない。

単純なカーブのプロットの軸を回転させる。

figure_1-6.png

とりあえず、使ってみる。

pythonは2.7。
必要なライブラリ。

  • numpy
  • matplotlib
  • mpl_toolkits

バージョンは知らん。

このコードで、動くはず。

import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.transforms import Affine2D


#rotation angle
rads=45
#data
v0 = np.linspace(0,2*np.pi*7,100)
v1 = np.cos(v0)
# figure, subplots
fig = plt.figure()

#floatingaxes
plot_extents = min(v0)-1, max(v0), min(v1)-0.5, max(v1)+0.5#max and min of x and y
transform = Affine2D().rotate_deg(rads)
helper = floating_axes.GridHelperCurveLinear(transform, plot_extents)
ax1 = floating_axes.FloatingAxes(fig,[0.2,0.2,0.8,0.7], grid_helper=helper)
ax11=fig.add_axes(ax1)
ax111 = ax1.get_aux_axes(transform)

#plot img
curv1 = ax111.plot(v0, v1, clip_on=False)
#################################
#numbers of ticks and ticklabels#
#################################
helper.grid_finder.grid_locator1._nbins = 10 #horizontal ax
helper.grid_finder.grid_locator2._nbins = 2 #vertical ax

ax11.axis["left"].label.set_text("vertical ax")
ax11.axis["bottom"].label.set_text("horizontal ax")


plt.show()

get_aux_axesしないとだめ。やらなかった場合、カーブを手動で回転させるようなtransformをさせる必要がある。しかも、軸も回転しているので、それも考慮して。単純なプロットの場合は、おとなしくget_aux_axesされたし。
やらないとどうなるか。

figure_1-3.png

はみ出る。
回転させた軸でなく、元の軸を基準に、プロットされている。

引数に、clip_on=Falseしておけば、はみ出してもplotされるので、どうはみ出たのかが分かる。

「表示されない」、「部分的に表示される」などで、意図した画像が出てこない場合、
何が起きているのかが分からないと、解決に時間がかかる。

figure_1-4.png

はみ出ている様子を見れば、「あ、なんか変形してない…」と分かる。

imshowで画像を表示しようとしても何も出てこない。

単純にimshowをすると、画像がでてこない。軸すら出てこない。コードは最後のやつしか載せない。

figure_1-5.png

なにも表示されないの図。



回転させた軸の情報が必要。

  1. img = ax11.imshow(… する。extentでset_xlimなどの代わりにする。
  2. 回転させた軸のtransDataを取得。
  3. イメージデータのset_transformを使って、2を適用する。
figure_1-10.png
出た。しかし、正しく表示されていない。

affineで画像を回転させて、解決。

imshowした後、

  1. ゆがめて
  2. 回転+回転済みの軸情報を追加
    させる必要がある。

そこで、Affine2Dを使う。
このページを参考に、affineを適用してみた。

  1. img = ax11.imshow(… する。extentでset_xlimなどの代わりにする。
  2. img.get_extent()で、extentを得る。
  3. 2.で得たextentを、img._image_skew_coordinate = (x1,y2)
  4. 回転軸の変形情報(transData)に、rotate_deg(deg角度(ラジアンじゃない))で、回転行列情報を加えたものを作る。
  5. イメージデータのset_transformを使って、4を適用する。

できた。

figure_1-12.png

set_ylim, set_xlimを使ってはいけない。

ここで、いつも通り、set_ylimとか使うと、おかしなことになる。
理由は説明できない。
自分は、set_ylim, set_xlimを入れていたのが原因だと知らず、かなり時間を消費してしまった。

「もしかしてaffineの軸が、z軸と入れ替わってる?」とか思って、いろいろ手動で回転行列を試したものの、うまくいかず断念。
「もう諦めよう…」と思って、むしゃくしゃして色々消してたら、偶然set_ylim、set_xlim原因だと特定できた。

figure_1-16.png

こんな風になった。

軸の色の変更

ここから、あとちょっと色を変えるだけだと思っていたが、甘かった。
いつもの軸の色を変えるコマンドが効かない。
google検索して出てきた結果を使っても、効かない。

figure_1-15.png

こうしたいのに、

figure_1-14.png

こうなる。

何がうまくいかなかったのかは、下記コードのコメントアウトしてある所がそれにあたるので、そこを見て下さい。

ちなみに、下記コードでは、画像を変えてしまいました。理由は、縦方向への変化が乏しかったからです。そのほかの画像は、面倒くさいので、変えませんでした。

figure_1-18.png

import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.transforms import Affine2D
import matplotlib.transforms as mtransforms

#rotation angle
rads=45
#data
n=32
m=16

"""
v0 = np.linspace(0,2*np.pi*n)
v1 = np.cos(v0)
v2 = [v1 for i in range(m)]
"""
v0 = [np.linspace(0, 2*np.pi*i, n) for i in range(m)]
#v1 = np.cos(v0)
v2 = np.cos(v0)#[v1 for i in range(m)]


# figure, subplots
fig = plt.figure()


#floatingaxes
plot_extents = 0, n, 0, m#max and min of x and y
transform = Affine2D().rotate_deg(rads)
helper = floating_axes.GridHelperCurveLinear(transform, plot_extents)
ax1 = floating_axes.FloatingAxes(fig,[0.2,0.2,0.8,0.7], grid_helper=helper)
ax11=fig.add_axes(ax1)
#ax111 = ax1.get_aux_axes(transform)

#plot img
img = ax11.imshow(v2, extent=[0,n,0,m], interpolation='none', clip_on=False, cmap="Reds")

x1, x2, y1, y2 = img.get_extent()
img._image_skew_coordinate = (x2, y1)

transform2 = mtransforms.Affine2D().rotate_deg(rads) + ax11.transData
img.set_transform(transform2)


#################################
#numbers of ticks and ticklabels#
#################################

helper.grid_finder.grid_locator1._nbins = 10 #horizontal ax
helper.grid_finder.grid_locator2._nbins = 2 #vertical ax

ax11.axis["left"].label.set_text("vertical ax")
ax11.axis["bottom"].label.set_text("horizontal ax")

#############################
# Use "extent" not set_ylim #
#############################
"""
ax11.set_ylim(m,0)
ax11.set_xlim(0,n)
"""


#########
# spine #
#########
"""
# it does not work.
ax11.spines['bottom'].set_edgecolor([1,0,0])
ax11.spines['top'].set_edgecolor([1,0,0])
ax11.spines['left'].set_edgecolor([1,0,0])
ax11.spines['right'].set_edgecolor([1,0,0])
"""
# it works.
ax11.axis["left"].line.set_color([1,1,0])
ax11.axis["right"].line.set_color([1,1,0])
ax11.axis["top"].line.set_color([1,1,0])
ax11.axis["bottom"].line.set_color([1,1,0])

#########
# ticks #
#########
ax11.tick_params(axis='x', colors=[1,1,0])
ax11.tick_params(axis='y', colors=[1,1,0])

##########
# labels #
##########
ax11.xaxis.label.set_color([1,1,0])
ax11.yaxis.label.set_color([1,1,0])

##############
# ticklabels #
##############
ax11.axis["left"].major_ticklabels.set_color([1,1,0])
ax11.axis["bottom"].major_ticklabels.set_color([1,1,0])

plt.show()

figure_1-17.png

ダメ押しで最後の一枚

ax111 = ax1.get_aux_axes(transform)
ax111.plot(v0,v1+5,color=[1,1,1],lw=3)

を、plt.show()の前に入れれば、画像の上に、プロットできる事が分かる。
線積分の作図で役立つのではなかろうか…

いや、線積分を作図する際は、ぜひ回転していない軸でお願いします。

これまでにも何回かやろうとした

floating_axes自体には、1年前位から気付いてはいた。
が、その当時、画像の回転と、軸の色の問題で引っかかって諦めた。

これまでの自分と違った所は、

  1. インスタンスをしらみつぶしに試す。
  2. ライブラリの中身を覗く努力。
    をしたことだった。

…結局、壮大な時間の無駄使いをしました。
三連休全部つぶした。
こんなくだらない事で、時間を無駄にして欲しくないと思い記す。

このブログに関して

『ブログは1000文字以上でないと、クローラが「価値あるサイト」だと判断しない→検索上位に上がらない』とどこかで見たことがある。なので、頑張って1000文字以上になるようにいろいろ考えていた。

今現在、グダグダ書くと、1000文字を軽く超えてしまう。
これも、コード抜きで2000文字位いっている。
コード入れると5000文字いっている。

むしろ、1000文字以下の方が、まとまっていてよろしいのではないのか。クローラ云々ではなく。

グダグダ書くと、読みにくくなるディメリットがあるが、
その一方でメリットもある。それは、

  • 見直しに時間がかからない。(見直さない。)
  • 疲れない。
  • 長続きする!

このブログのモットーだ。

見直す必要なんかない。

正直、何度も見直す必要なんてない。
出力される絵と、プログラムとが大事なわけだから。
日本語がおかしくても、コードが動けばいいわけだし。
そもそも、コードだって動かなくったって、ネットに書いてある適当な情報なわけだし。

1000文字超えているが…

1000文字超えさせようとしたのは、アクセス数を稼ぐため。
アクセス数を稼ごうとしたのは、アフィのため。

しかし、アクセスカウンターが30000超えていますが、アフィリエイトでは1円も入ってきていません。
(かなり前の二百数十円が、延々繰り越されている。)

だれか、横の楽天のアドから業務用チーズとかチョコを買ってくれ。(PC版でしか見えないかもしれない)

posted by yuchan at 07:00 | Comment(0) | python

20180128

matplotlib plotの色を、値によって変える。

plotに、値によるグラデーションをつける。

imshow(二次元、画像)で作図したとき、一次元の線にも、値によって色を着けたいと思った。
必ずしも必要ないし、むしろ分かりにくくなる場合の方が多い。しかし、やってみた。

最終目標はコレ。

007.png

今のところ、欠点として、2点間プロットのどちらかの値で着色するので、同じ高さの線が、違う色で着色されている。。。
二つの値の平均をとるべきでしたね。すいません。

2つのやり方を見つけた。

  1. colorcycleの設定を行う。
  2. plot時に、colorを指定する。

両方とも手間は同じくらい。
両方とも、cmapを使う事になる。

clorcycleとは、

線の色を変えるだけなら、プロットの度に自動で切り替わる。

000.png
このサイクルが、colorcycle

import matplotlib.pyplot as plt
import math
import matplotlib

N=128f = 8
omg = [i*f*2*math.pi/N for i in range(N)]
sig = [math.sin(i) for i in omg]

fig,(ax) = plt.subplots(1,1)

for j in range(len(sig)-1):
ax.plot(omg[j:j+2], sig[j:j+2])

plt.show()

このプロットの度に色が変わるサイクルが、colorcycleで設定できる。これを手動で設定するのが「1」のやり方。
colorcycleの次の色は、下記で見れる。

ax._get_lines.get_next_color()

colorcycleを設定するやり方

色は、colorcycleによって変更できる。

RGBA=[1,0,0,1]
ax.set_color_cycle(RGBA)

こんな感じで。

使ってみると、こんな感じになる。

import matplotlib.pyplot as plt
import math
import matplotlib


N=128
f = 8
omg = [i*f*2*math.pi/N for i in range(N)]
sig = [math.sin(i) for i in omg]

fig,(ax) = plt.subplots(1,1)
colormap = plt.get_cmap('jet')

ax.set_color_cycle([colormap(k) for k in sig])

for j in range(len(sig)-1):
ax.plot(omg[j:j+2], sig[j:j+2])

plt.show()

0以下が、同じ色になってしまっているのは、なんか変ですね。

003.png

各cmapは、データ→[R,G,B,A]に変える関数としても使える。

すでに上でも使ったが、カラーマップというのがある。
imshowを使う際は、画像の着色の仕方を指定したりする「何か」と認識していた。
実は、このcmapは、RGBAを返す関数としても使える。
これを使って、「2」のやり方を実現する。

とりあえずRGBAに変える関数としてcmapを使ってみる。

-1から1までの数値を、cmapに入れたら、どうなるのか。

import matplotlib.pyplot as plt

cm = plt.get_cmap("jet")#jetの色を返す関数
print cm([i/100. for i in range(101)])

こうなりました。

[[ 0.00000000e+00 0.00000000e+00 5.00000000e-01 1.00000000e+00]
[ 0.00000000e+00 0.00000000e+00 5.35650624e-01 1.00000000e+00]
(省略)
[ 5.35650624e-01 0.00000000e+00 0.00000000e+00 1.00000000e+00]
[ 5.00000000e-01 0.00000000e+00 0.00000000e+00 1.00000000e+00]]

ちゃんと、RGBAのリストが返ってきた。
これを、plotの関数の、引数colorのなかにいれるだけ。

import matplotlib.pyplot as plt
import math
import matplotlib


N=128f = 8
omg = [i*f*2*math.pi/N for i in range(N)]
sig = [math.sin(i) for i in omg]

fig,(ax) = plt.subplots(1,1)
colormap = plt.get_cmap('jet')
cm = colormap(sig)

for j in range(len(sig)-1):
ax.plot(omg[j:j+2], sig[j:j+2],color=cm[j])

plt.show()

003.png

やっぱり0以下が変ですね。
0以下、1以上が、同じ色になる。

正規化が必要。

0以下が表示されないのは、cmapは0から1までで正規化された値にしか対応していないから。
範囲がmin. -1, max. 1のデータを、min. 0,max 1のデータに書き直す(0~1のデータに正規化する)必要がある。

ここに、使用できる正規化の一覧があるのと、
ここで、具体例がある。

  1. プロットする変数を用意する。
  2. cmapを用意する。
  3. 変数を正規化する。
  4. cmapに3の変数を入れる。

カラーマップのほうを変えるのではなく、入力データの方を変える。
データが変わっちゃうじゃん。なんか気持ち悪い。

import matplotlib.pyplot as plt
import matplotlib as mt

data = [i/10.0 -1 for i in range(21)]
cm = plt.get_cmap("jet")
norm = mt.colors.Normalize(vmin=-1, vmax=1)#正規化 これに入れたデータは、cmapのリミットは-1to1になる。

print data#-1 から 1までのデータ。
print cm(data)#データをカラーマップで
print cm(norm(data))

>>> print data#-1 から 1までのデータ。
[-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.30000000000000004, -0.19999999999999996, -0.09999999999999998, 0.0, 0.10000000000000009, 0.19999999999999996, 0.30000000000000004, 0.3999999999999999, 0.5, 0.6000000000000001, 0.7, 0.8, 0.8999999999999999, 1.0]
>>> print cm(data)#データをカラーマップで
[[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.5 1. ]
[ 0. 0. 0.9456328 1. ]
[ 0. 0.3 1. 1. ]
[ 0. 0.69215686 1. 1. ]
[ 0.16129032 1. 0.80645161 1. ]
[ 0.49019608 1. 0.47754586 1. ]
[ 0.80645161 1. 0.16129032 1. ]
[ 1. 0.77051561 0. 1. ]
[ 1. 0.40740741 0. 1. ]
[ 0.9456328 0.02977487 0. 1. ]
[ 0.5 0. 0. 1. ]]
>>> print cm(norm(data))
[[ 0. 0. 0.5 1. ]
[ 0. 0. 0.71390374 1. ]
[ 0. 0. 0.9456328 1. ]
[ 0. 0.09607843 1. 1. ]
[ 0. 0.3 1. 1. ]
[ 0. 0.50392157 1. 1. ]
[ 0. 0.69215686 1. 1. ]
[ 0. 0.89607843 0.97090449 1. ]
[ 0.16129032 1. 0.80645161 1. ]
[ 0.3257432 1. 0.64199873 1. ]
[ 0.49019608 1. 0.47754586 1. ]
[ 0.64199873 1. 0.3257432 1. ]
[ 0.80645161 1. 0.16129032 1. ]
[ 0.97090449 0.95933188 0. 1. ]
[ 1. 0.77051561 0. 1. ]
[ 1. 0.58169935 0. 1. ]
[ 1. 0.40740741 0. 1. ]
[ 1. 0.21859114 0. 1. ]
[ 0.9456328 0.02977487 0. 1. ]
[ 0.71390374 0. 0. 1. ]
[ 0.5 0. 0. 1. ]]
>>>

とすれば、normによって、cmap全域のRGBAが得られているのは見える。
normが、-1から+1のdataを、0から+1のデータに変形させた。
normは、0から1までのデータに置き換えるときの、やり方を指定している。

import matplotlib as mt

data = [i/10.0 -1 for i in range(21)]
norm = mt.colors.Normalize(vmin=-1, vmax=1)#正規化 これに入れたデータは、cmapのリミットは-1to1になる。
print dataprint norm(data)

>>> print data
[-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.30000000000000004, -0.19999999999999996, -0.09999999999999998, 0.0, 0.10000000000000009, 0.19999999999999996, 0.30000000000000004, 0.3999999999999999, 0.5, 0.6000000000000001, 0.7, 0.8, 0.8999999999999999, 1.0]
>>> print norm(data)
[ 0. 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55
0.6 0.65 0.7 0.75 0.8 0.85 0.9 0.95 1. ]
>>>

引数のvmin,vmaxは、imshowの時にも使う。
あれは、normalizeの関連の引数だったんだ…

ややこしい。

  • プロット時は、生データを使いたい。
  • cmapは、0to1のデータしか受け付けない。
  • 入力データの形を正規化してしまうと、カラーバーの表示と、表示したいデータとの整合性が取れなくなる。
  • imshowは自動的にnormalizeしてくれる(ことが多い。)

不都合は…特にないか。?もし、カラーバーを表示しようとなると、ちょっと大変なのかもしれない…
ごちゃごちゃしてよく分からない。

設定できるのは、最大値、最小値だけではない。
値の増え方、色の変わり方の関係を非線形にできる。

ここで線形っていうのは、直線だから、線形。直線じゃないと、非線形。

20180127210724.png

先ほどのnormは線形。線形なら、二点vminとvmaxが決まれば、値の移り変わり-色の移り変わりが決まる。


非線形っていうのは例えばlogとかだろうよ。
対数グラフであれば、こちらも2点が決まれば、形が決まる事が分かる。

20180127230253.png


とりあえず、colorcycleを修正

import matplotlib.pyplot as plt
import mathimport matplotlib

N=128
f = 8
omg = [i*f*2*math.pi/N for i in range(N)]
sig = [math.sin(i) for i in omg]

fig,(ax) = plt.subplots(1,1)
colormap = plt.get_cmap('jet')
norm = matplotlib.colors.Normalize(vmin=min(sig), vmax=max(sig))
ax.set_color_cycle([colormap(norm(k)) for k in sig])

for j in range(len(sig)-1):
ax.plot(omg[j:j+2], sig[j:j+2])

plt.show()

007.png

plotの引数color版も修正。

plotに入れるか、set_color_cycleに入れるかの違いでしかありませんが、

import matplotlib.pyplot as plt
import math
import matplotlib

N=128
f = 8
omg = [i*f*2*math.pi/N for i in range(N)]
sig = [math.sin(i) for i in omg]

fig,(ax) = plt.subplots(1,1)
colormap = plt.get_cmap('jet')
norm = matplotlib.colors.Normalize(vmin=min(sig), vmax=max(sig))
c = colormap(norm(sig))

for j in range(len(sig)-1):
ax.plot(omg[j:j+2], sig[j:j+2], color=c[j+1])

plt.show()

トップに載せたのは、上記コードの画像。


いちいち、normするのが面倒。cmapの引数でvminvmaxが選べればいいのに。

この機能は、次の次の動画くらいで使った。

posted by yuchan at 07:00 | Comment(0) | python

20171230

Pythonで、「二次元フーリエ変換した後、象限を取り換える」関数を作った。

二次元フーリエ変換

二次元のフーリエ変換の、象限を取り換える関数を作った。画像の2行目と4行目のsubplot内のインセットは、象限を取り換えている。2行目は、何をやっているのかわかりやすくするために、あえてやった。

figure_4.png

# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import tkFileDialog as tk

name = tk.askopenfilename()

##############################
#### 象限を取換える関数。#####

##############################
def quadrant(img):
n = len(img[0])
m = len(img)
list1 = range(n)[n/2:] + range(n/2)
list2 = range(m)[m/2:] + range(m/2)
a = [[img[j][i] for i in list1] for j in list2]
return a

######################################
##### ここだけ書くつもりだったが #####
##### ながくなってしまった。 #########
######################################


img1 = mpimg.imread(name)
img1 = np.mean(img1, -1)# グレイにした


img2 = quadrant(img1)# 二次元フーリエ変換

dft1 = np.fft.fft2(img1)# 実部、虚部と絶対値の取り出し。
dft2 = dft1.real
dft3 = dft1.imag
dft4 = np.abs(dft1)

# 象限の取換え(必要ないと思った)
quad2 = quadrant(dft2)
quad3 = quadrant(dft3)
quad4 = quadrant(dft4)


f,((ax11,ax12,ax13),(ax1,ax2,ax3),(ax4,ax5,ax6),(ax7,ax8,ax9)) = plt.subplots(4,3)


ax11.imshow(img1,cmap="gray")

ax1.imshow(img2,cmap="gray")
ax2.set_axis_off()
ax3.set_axis_off()

ax4.imshow(dft2,vmin=-3000,vmax=3000,cmap="gray")
ax5.imshow(dft3,vmin=-3000,vmax=3000,cmap="gray")
ax6.imshow(dft4,vmin=-3000,vmax=3000,cmap="gray")

ax7.imshow(quad2,vmin=-3000,vmax=3000,cmap="gray")
ax8.imshow(quad3,vmin=-3000,vmax=3000,cmap="gray")
ax9.imshow(quad4,vmin=-3000,vmax=3000,cmap="gray")

ax11.set_title("image")
ax1.set_title("arrange quadrant")
ax4.set_title("2dfft real part")
ax5.set_title("2dfft imaginary part")
ax6.set_title("2dfft absolute value")

ax12.set_axis_off()
ax13.set_axis_off()

plt.show()

tkFileDialogを使ったので、ほかの画像でも試せる。
必要なpythonモジュール:matplotlibやnumpy

一番難しかったところ。

二次元フーリエ変換の結果を表示でてこずった。

エラーが起きて、表示できない。

dft結果は複素数だから。

画像の配列の各要素に入っている値を、実数、虚数、絶対値別に変換してから再表示。

表示された画像が、単一色(単色で真っ青だったり真っ赤だったりした。)
値をprintしても、かなりバリエーションあるのに何で…

max,min値だけが、(他の画像行列の要素と比べ)異常に高いため他の(絶対値が)小さい値はフラットに見える。

img配列の値を参考に、imshowの引数vmin,vmaxに適切な値を入れる。(どんな値が適切かは画像サイズ等による。)

表示された!

象限を取り換える操作は、「不必要なんじゃないか?」と思っているので、この記事のタイトルは、ただの「pythoonで二次元フーリエ変換」でいい気がする。面倒くさいのでそのままにする。

続きを読む
posted by yuchan at 07:00 | Comment(0) | python

20171112

matplotlibのlinestyleのバリエーション

見たこと無いlinestyleがある…

"linestyle"でgoogle検索すると、公式のページに、見慣れないlinestyleがある。

文字列タプル
'solid'(0, ())
'loosely dotted'(0, (1, 10))
'dotted'(0, (1, 5))
'loosely dashed'(0, (5, 10))
'dashed'(0, (5, 5))
'loosely dashdotted'(0, (3, 10, 1, 10))
'dashdotted'(0, (3, 5, 1, 5))
'densely dashdotted'(0, (3, 1, 1, 1))
'loosely dashdotdotted'(0, (3, 10, 1, 10, 1, 10))
'dashdotdotted'(0, (3, 5, 1, 5, 1, 5))
'densely dashdotdotted'(0, (3, 1, 1, 1, 1, 1))

実行してみると、エラーが返ってくる。

import matplotlib.pyplot as plt
plt.plot(range(11),range(11),linestyle="loosely dashed")
plt.show()

>>> import matplotlib.pyplot as plt

>>> plt.plot(range(11),range(11),linestyle="loosely dashed")

#長いので省略#

ValueError: You passed in an invalid linestyle, `loosely dashed`.  See docs of Line2D.set_linestyle for valid values.

そんなlinestyleないってよ…

ドキュメントちゃんと読めってよ…

新しいバージョンにしか対応していないんじゃなくて、”loosely ~”とかは、上記サイト内に記載されたスクリプトの設定。 重要なのは、横の数字(タプル型変数)。

引数linestyleはタプルもとれた!

衝撃的事実。文字列("dashed"とか)だけじゃないという… linestyleの文字列の引数に頼る必要は無い。オリジナルの線が描ける。

黒、白と交互に指定する。

引数のタプルは、黒、白交互に長さを設定する。 例えば、点線なら

import matplotlib.pyplot as plt
plt.plot(range(11),range(11),linestyle=(0,(1,1)))
plt.show()
figure_1.png
白黒で印刷するときに、マーカー以外のチョイスができた。プロットの選択肢の幅が広がった。

数字は、長さを表す。

import matplotlib.pyplot as plt
plt.plot(range(11),range(11),linestyle=(0,(10,20)))
plt.show()
figure_10-20.png

数値は何個でも入れられる

コンマで区切って、何個でも引数を入れられる。(偶数個に限る)

import matplotlib.pyplot as plt
plt.plot(range(11),range(11),linestyle=(0,(10,20,30,40)))
plt.show()
figure_10-20-30-40.png

黒1、白2、黒3、白4の長さで繰り返す。 自分で長さを調節できる。

1個目の引数を変えると…

縦長にならなかった…一個目の引数は、オフセットだそうな。

import matplotlib.pyplot as plt
plt.plot(range(11),range(11),linestyle=((3,6),(1,1))
plt.show()

このhtmlをほとんど80%スマホから作った!

  1. epsilon(マークダウンエディタ。ロシア製だが気にしない。)でマークダウンで書く。
  2. htmlでexport。tex部分はいじらないでおいてくれるので嬉しい。下手に画像化されると、劣化して汚い。転送も面倒。mathjaxは既に導入済みのサイトなので、書いたのをそのまま入れてくれた方が楽。
  3. htmlをサイトへコピペ。
  4. PCから微修正。

スマホで100%やりたい。

pythonコードの確認は、PC環境が必要だった。だから、20%位はPCを使った。 google playには、qpythonとかある。しかし、公式じゃないので得体が知れない。だからandroid上で動くpython環境を出して欲しい。

スマホでnumpy, matplotlibなどを実行したい。そうすれば、電車の中で内職できる。家だって、PC使えるのに、なぜか携帯触ってる。PCのほうが楽なはずなのに…立ち上げたり準備があるのが嫌なんだと思う。ファミコンのように、スイッチ一つですぐ動く感覚じゃないと。


続きを読む
posted by yuchan at 00:00 | Comment(0) | python

20170806

どちらが正しいRGBか。(matplotlibでcmapをいじる)

 

matplotlibのカラーマップを自分で作成する時に起こった問題。

20170805-05.png

 

中段と下段、二種類のRGBの表現方法があることに気づいた。
ディスプレイ上で、RGBを正しく表現しているのは、中段と下段のどちらか。(RGBAではない。)

答えは下段…だと自分は思う。
ただ、写真的には、中段だと思う。

pythonで画像を表示する。

pythonで画像を表示するとき、自分は、matplotlibかpqtgraphを使う。(ほかにもいろいろ方法はある)
両方とも、数値をプロットしたりするときに使うソフト。

詳しい使い方は、ここにある。使う度に毎回参照している。多分一生覚えられない。

例えば、以下のようなコードを実行したとき、
ファイルダイアログが開いて、任意のファイルを開き、画像を表示する。

# coding: Shift_JIS

import matplotlib.pyplot as plt
# 画像表示用の関数1
import matplotlib.image as mpimg
# 画像表示用の関数2
import tkinter.filedialog as tkfd
# ファイルダイアログ用の関数。

f, (ax1) = plt.subplots(1)
# 作図
filename = tkfd.askopenfilename()
#ファイルダイアログを出し、選んだファイルを変数に代入
img1 = mpimg.imread(filename)
#画像
ax1.imshow(img1)
plt.show()

20170805-00.png

これを、RGB別に分けて表示するには、直感的にはRGBの情報が入った配列(np.array)をこうやってとりだしたい。

img[:,:,0]

上記を使って、下記のようなコードを実行すると、

import numpy as np
# 数値計算ライブラリ
import matplotlib.pyplot as plt
# 画像表示用の関数1
import matplotlib.image as mpimg
# 画像表示用の関数2
import tkinter.filedialog as tkfd
# ファイルダイアログ用の関数。
f, ((ax11,ax12,ax13,), (ax21,ax22,ax23,)) = plt.subplots(2,3)
# 作図
ax11.axis("off")
ax13.axis("off")
filename = tkfd.askopenfilename()
#ファイルダイアログを出し、選んだファイルを変数に代入
img1 = mpimg.imread(filename)

ax12.imshow(img1)
ax21.imshow(img1[:,:,0])
ax22.imshow(img1[:,:,1])
ax23.imshow(img1[:,:,2])
plt.show()

こんな感じになる。

20170805-01.png

色が付いている。
これは、カラーマップが、良くない。カラーマップがjetというやつだから、こうなった。

cmapとは

前の例を、カラーマップを指定すると、こんな感じにできる。

import numpy as np 
# 数値計算ライブラリ
import matplotlib.pyplot as plt
# 画像表示用の関数1
import matplotlib.image as mpimg
# 画像表示用の関数2
import tkinter.filedialog as tkfd
# ファイルダイアログ用の関数。
f, ((ax11,ax12,ax13,), (ax21,ax22,ax23,)) = plt.subplots(2,3)
# 作図
ax11.axis("off")
ax13.axis("off")
filename = tkfd.askopenfilename()
#ファイルダイアログを出し、選んだファイルを変数に代入
img1 = mpimg.imread(filename)
#画像
ax12.imshow(img1)

ax21.imshow(img1[:,:,0], cmap="hot")
ax22.imshow(img1[:,:,1], cmap="summer")
ax23.imshow(img1[:,:,2], cmap="winter")

ax21.set_title("hot")
ax22.set_title("summer")
ax23.set_title("winter")

plt.show()

下側の、図の、タイトルが、cmap。

20170805-02.png

ぱっと見は、これで完成しているように見えるが…
これじゃない。こうじゃないんだよ!

まず

  1. 色が濃いはずの部分が、薄くなっている!!
  2. 色が赤青緑ではない!!!!!!!!!!!

白→赤ないし、黒→赤みたいになってないとダメ。できあいのカラーマップじゃダメ。

なので、任意の二色のグラデーションを作りたい。

matplotlibでの解決方法

公式ではcmapの作り方はここら辺を参考にできる
が、cdictの中身の意味はここを見る必要があるし、公式よりもここの方が参考になるかもしれない。

import matplotlibimport numpy as np 
# 数値計算ライブラリ
import matplotlib.pyplot as plt
# 画像表示用の関数1
import matplotlib.image as mpimg
# 画像表示用の関数2
import tkinter.filedialog as tkfd
# ファイルダイアログ用の関数。
cdict = {'red': ((0.0, 0.0, 0.0),(1.0, 1.0, 1.0)),
'green': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0)),
'blue': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0))
}
cmap_RtoBRK = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict,256)

cdict2 = {'red': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0)),
'green': ((0.0, 0.0, 0.0),(1.0, 1.0, 1.0)),
'blue': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0))
}
cmap_GtoBRK = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict2,256)

cdict3 = {'red': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0)),
'green': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0)),
'blue': ((0.0, 0.0, 0.0),(1.0, 1.0, 1.0))
}
cmap_BtoBRK = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict3,256)

f, ((ax11,ax12,ax13,), (ax21,ax22,ax23,)) = plt.subplots(2,3)
# 作図

ax11.axis("off")
ax13.axis("off")
filename = tkfd.askopenfilename()
#ファイルダイアログを出し、選んだファイルを変数に代入
img1 = mpimg.imread(filename)#画像

ax12.imshow(img1)
ax21.imshow(img1[:,:,0], cmap=cmap_RtoBRK)
ax22.imshow(img1[:,:,1], cmap=cmap_GtoBRK)
ax23.imshow(img1[:,:,2], cmap=cmap_BtoBRK)

plt.show()

20170805-03.png

黒なんか指定した覚えはない。
白い紙に、絵の具を置くと、こうなるでしょ!?
という人は、こっちがいいかも…

import matplotlibimport numpy as np 
# 数値計算ライブラリ
import matplotlib.pyplot as plt
# 画像表示用の関数1
import matplotlib.image as mpimg
# 画像表示用の関数2
import tkinter.filedialog as tkfd
# ファイルダイアログ用の関数。
cdict = {'red': ((0.0, 1.0, 1.0),(1.0, 1.0, 1.0)),
'green': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0)),
'blue': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0))
}
cmap_RtoBRK = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict,256)

cdict2 = {'red': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0)),
'green': ((0.0, 1.0, 1.0),(1.0, 1.0, 1.0)),
'blue': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0))
}
cmap_GtoBRK = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict2,256)

cdict3 = {'red': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0)),
'green': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0)),
'blue': ((0.0, 1.0, 1.0),(1.0, 1.0, 1.0))
}
cmap_BtoBRK = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict3,256)

f, ((ax11,ax12,ax13,), (ax21,ax22,ax23,)) = plt.subplots(2,3)
# 作図

ax11.axis("off")
ax13.axis("off")
filename = tkfd.askopenfilename()
#ファイルダイアログを出し、選んだファイルを変数に代入
img1 = mpimg.imread(filename)
#画像

ax12.imshow(img1)
ax21.imshow(img1[:,:,0], cmap=cmap_RtoBRK)
ax22.imshow(img1[:,:,1], cmap=cmap_GtoBRK)
ax23.imshow(img1[:,:,2], cmap=cmap_BtoBRK)

plt.show()

20170805-04.png

 

gifアニメで説明すると、

cdictの中身の意味は前述のここを見る必要がある

作り方のコードは、あえて載せない。
理由は、説明が面倒だから。
(matplotlibが、imagmagicを参照していて、それのバージョンやらインストールする場所とか…が面倒だから、説明しにくい。)

クリックすると、若干画像の質がアップするので、クリックされたし。
ani07.gifani07.gif
ani07.gif
背景白の方は、数値が減ると赤以外の色の値が減少(データが小さいと、RGB揃って、全体が白に近づく。Rは一定、G,Bが減少する。)
背景黒の方は、数値が大きくなると、赤色の値が大きくなる。

赤がない部分は、ディスプレイのLEDが消えているので黒。こう考えると、背景黒の方が、赤の増加を数値で表現しているという点で、正しい。

自分は、「白い紙に色を塗る派」なので、背景白が正としたい所だが…

どちらが正しいのか

黒→赤とかが、本来の表現だが…
なぜならば、PCやら、携帯やらをonしていない時、そのディスプレイは、大抵黒。
off時に、黒以外の色になるディスプレイなんて、ブラウン管TVが灰色だったり、GBAとかワンダースワン以来、お目にかかっていない。
ここに、3色のLEDとか液晶とか何かが光って、画面を表示している。
3色全部つくと、白くなる。この光の強さと、値の大きさを対応させているから、黒→色が正しい表現。

しかし、学校とかでは、絵を描く時に、白い紙を配られて、そこに色を置いた。つまり、何もないところは白で、色があるところは赤。
この考え方で行くと、白い紙の上では、白→色が正しい表現になる。

紙と、ディスプレイで、表現の方法が違う。

紙の場合は、光っていない。
ディスプレイの場合は光っている。
この差は大きい。

ただ、PCはディスプレイで見るとはいえ、紙的な表現を心掛けた方がいいと思っている。
なぜならば、印刷時のインク消費量が上がるから。

import matplotlibimport numpy as np
# 数値計算ライブラリ
import matplotlib.pyplot as plt
# 画像表示用の関数1
import matplotlib.image as mpimg
# 画像表示用の関数2
import tkinter.filedialog as tkfd
# ファイルダイアログ用の関数。
cdict = {'red': ((0.0, 1.0, 1.0),(1.0, 1.0, 1.0)),
'green': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0)),
'blue': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0))
}
cmap_RtoBRK = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict,256)

cdict2 = {'red': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0)),
'green': ((0.0, 1.0, 1.0),(1.0, 1.0, 1.0)),
'blue': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0))
}
cmap_GtoBRK = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict2,256)

cdict3 = {'red': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0)),
'green': ((0.0, 1.0, 1.0),(1.0, 0.0, 0.0)),
'blue': ((0.0, 1.0, 1.0),(1.0, 1.0, 1.0))
}
cmap_BtoBRK = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict3,256)

cdict12 = {'red': ((0.0, 0.0, 0.0),(1.0, 1.0, 1.0)),
'green': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0)),
'blue': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0))
}
cmap_RtoBRK2 = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict12,256)

cdict22 = {'red': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0)),
'green': ((0.0, 0.0, 0.0),(1.0, 1.0, 1.0)),
'blue': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0))
}
cmap_GtoBRK2 = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict22,256)

cdict32 = {'red': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0)),
'green': ((0.0, 0.0, 0.0),(1.0, 0.0, 0.0)),
'blue': ((0.0, 0.0, 0.0),(1.0, 1.0, 1.0))
}
cmap_BtoBRK2 = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict32,256)

f, ((ax11,ax12,ax13), (ax21,ax22,ax23), (ax31,ax32,ax33)) = plt.subplots(3,3)
# 作図

ax11.axis("off")
ax13.axis("off")

filename = tkfd.askopenfilename()
#ファイルダイアログを出し、選んだファイルを変数に代入

img1 = mpimg.imread(filename)
#画像
ax12.imshow(img1)
ax21.imshow(img1[:,:,0], cmap=cmap_RtoBRK)
ax22.imshow(img1[:,:,1], cmap=cmap_GtoBRK)
ax23.imshow(img1[:,:,2], cmap=cmap_BtoBRK)
ax31.imshow(img1[:,:,0], cmap=cmap_RtoBRK2)
ax32.imshow(img1[:,:,1], cmap=cmap_GtoBRK2)
ax33.imshow(img1[:,:,2], cmap=cmap_BtoBRK2)

plt.show()
この画像は、トップにのせた。



もうすぐ、新しいアニメが出せそうな予感がする。
今度は、フーリエ変換ではなく、クロソイド曲線についてアニメーションする。
plt-00588-20170731212136.png
その後で、二次元フーリエ変換についてもやる。
空いている部分に、行列の掛け算のアニメを入れる。
「行列を使った一次方程式の解き方」のやつを入れる。

それでも余った場合は、何かそれっぽいものを入れる。
posted by yuchan at 07:00 | Comment(0) | python

20170717

matplotlibのannotateのhead_widthを設定すると、エラーが返ってくる

matplotlibで矢印を描くときpyplot.annotateを使用する。

import matplotlib.pyplot as plt

plt.annotate("",
xy=(0.5, 0.5), xycoords='data',
xytext=(0, 0), textcoords='data',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3",
),
)

plt.show()

ここまでなら、公式サイトで手に入る。

20170716-02.png

pyplot.arrowよりも、こちらの方が、座標指定したところにきっちり線が書かれる点で優れている気がするが、煩雑なのが欠点。

矢じりの部分の大きさを変えたいと思ったので、検索すると、headwidth(もしくはhead_width)という引数を使うと変えられるような事が書いてあったので、実行したがエラーが返ってきた。
最終的には動かせるようになったので、メモ書きしておく。

症状、「headwidthなんてプロパティはない。」とエラーが返される。

import matplotlib.pyplot as plt

plt.annotate("",
xy=(0, v1), xycoords='data',
xytext=(0, v1*-1.0), textcoords='data',
arrowprops=dict(arrowstyle="->",head_width = 2, head_length = 1.5,connectionstyle="arc3")
)

plt.show()

まんま、ここの事ですが

AttributeError: Unknown property headwidth

もしくは

AttributeError: Unknown property head_width

が出てしまう。
「バージョンの違いか?それとも、head_widthとかスペルミスしてるのか?」
とか思ったが、使い方を間違えていただけだった。

headwidthは、annotateの中の引数ではない。

head_widthはどこに入れるべきだったのか?

headwidthは、

  1. annotateの中の、
  2. arrowpropsの中で、
  3. dict()の中の、
  4. arrowstyleの中に入れる

更に言うと、arrowstyleで

'<->’

とかを使用するのであれば、

'<->, head_width=15’

とかしなければならない。

お気づきでしょうか?全て文字列なのです。
headwidth=15とかの部分は、全て文字列として指定しなければならないのです。
矢印の先端の形状を決める引き数を書き終わったら、文字列を締めくくらずに、続けて書かないといけないのです。

正しいコードは

import matplotlib.pyplot as plt

plt.annotate("",
xy=(0.5, 0.5), xycoords='data',
xytext=(0, 0), textcoords='data',
arrowprops=dict(arrowstyle="->, head_width = 2, head_length = 3",
connectionstyle="arc3",
),
)

plt.show() 
20170716-01.png
こうなった。結構、不格好。
posted by yuchan at 07:00 | Comment(0) | python

20170505

matplotlibで、x軸とy軸の数字部分の大きさを変える

こういう時、役立つ

作図の際、縦横比が1から離れた数字になってしまい、それを変えたくない場合、こういう事が起こる。
こういう時、文字が狭くて被ってしまうのを、どうにかしたい。かといって、もう片方の軸の大きさは、ちょうどいいので変えたくない。

こういう時に片方だけ、小さくしたりできればいいなと思った。
20170504-03.png

labelとtickとaxisの見分けは付いているか。

一般的かどうかは知らないが、matplotlibでの呼び方を紹介すると、

20170504-01.png
赤で囲った、数字の部分は、tickという。(ラベルでも軸でもない。)
(もっというと、軸にくっついてる、ぴょこっと飛び出た線の部分がtick。)
軸の名前を、文字で説明している部分がlabel(図でいう、xlabelとか、ylabelとか書いてある部分)

上のグラフは、下のコードで描いたものを、ウィンドウズの「ペイント」で書き込んだ。

import numpy as np
import matplotlib.pyplot as plt

l = np.linspace(-15,15,101)

plt.plot(l,np.sin(l))
plt.xlabel("xlabel")
plt.ylabel("ylabel")

plt.show()

tickのy軸側の大きさだけを変えたい時は、

tickのy軸側の大きさだけを変えたい時がある。
チック部分のフォントが大きすぎて、上下に密着し過ぎる時。(かぶっている時)とか

plt.tick_params(labelsize=3)

としてしまうと、両方のフォントサイズが小さくなってしまう。
「x軸は、別に変えなくていいんだ…」と思っている時には、これでは困る。
こちらを使われたし。

plt.tick_params(axis='y', which='major', labelsize=3)

あえてまとめるとこう。

import numpy as np
import matplotlib.pyplot as plt

l = np.linspace(-15,15,101)

plt.plot(l,np.sin(l))
plt.xlabel("xlabel")
plt.ylabel("ylabel")

plt.tick_params(axis='y', which='major', labelsize=3)

plt.show()

20170504-02.png
片側だけ、小さくできました。

posted by yuchan at 07:00 | Comment(0) | python

20170405

VBAで、pythonのrangeとか、np.linspaceみたいな連番を得る

VBAを頑張っている。

pythonは、どんな環境でも使えるわけではない。

  • Windows環境下で、
  • Excelなど、Officeは使える。
  • マクロも使える。
  • コマンドラインも使える。
  • でも、pythonは使えない。
  • というか、インストールしていいのか分からない。

「この際、VBAを覚えて、pythonで培った作図スキルを、excelのVBAで活かそう。」という考え方で、勉強している。
VBA(Visual Basic for Application(「アプリ内のマクロを、VBの文法で実現」という認識))

VBAをpythonの様に使えない

pythonでいう、rangeとか、np.linspaceみたいな関数が無いと困る。
作図の時に、頻繁に使用するので。

で、VBAには、実際無い。
rangeすら無い。
rangeに至っては、excelのセルの範囲を指定するときの言葉の様です。

VBAで、連番出す系の関数がほしい。
そんな時のために、類似した関数を作った。

renban(最初の数字、最後の数字、配列個数)

こんな感じで使う。Subプロシージャの中で…

Sub test()
b = renban(0, 1, 10000)
Debug.Print b(777)
End Sub

とすると、bに入れた変数の777番目が取り出されて、

0.0777

が返ってくる。

連番で配列が得られれば、それを変数として関数にバンバン代入して、どんどんデータがたまっていき、ぱっぱと作図できる。

コード全文

Function renban(start1 As Double, stop1 As Double, num As Integer)
'型は敢えて指定しない。
Dim grd
'変数cは、引数numによって、長さが指定される。
'プロシージャ内(Functionとか、Subとかの中)で動的な変数(配列の長さが可変長)には、Dimじゃなく、ReDimが必要
ReDim c(num)
'引数start1〜stop1までの傾きgrdを求める。

grd = (stop1 - start1) / num

For i2 = 0 To num
c(i2) = grd * i2
Next i2

renban = c

End
Function

Sub test()
b = renban(0, 1, 100)
Debug.Print b(77)
End Sub

使いたいなら、毎回、Function~End Function部分を書けば、いいと思うよ。面倒ですが…

何かいいやり方があるのだろうか。

posted by yuchan at 07:00 | Comment(0) | python

20170214

matplotlibのaxes3Dで、aspectの設定がうまくできなかったので、行列とかをいじった。

まだ、フーリエ変換関係の動画を作ろうとしている。

  • DFT計算詳細
  • iDFT計算詳細
  • FFT
  • 2DDFT
  • i2DDFT

については、詳細動画作りたい。作ってて楽しいし。

その過程で、フーリエ変換の結果を、3Dで図示する必要があった。(Re, Im, kの3軸)

matplotlibのaxes3dを使った。

from mpl_toolkits.mplot3d.axes3d import Axes3D
from mpl_toolkits.mplot3d import proj3d

で、3Dプロットをしている。

この機能は、重なった部分を描画すると、最後に書いた形状が、常に手前に表示されるという未解決のバグがあるが、複雑な形状じゃなければさほど問題にならない。(もし問題があれば、mayaviを使う

軸のaspectがうまくいかなかった。

何も指定しないと、自動的に、余ったスペースにフィットする感じにプロットが拡大される。
しかし、縦横比が気に入らない。
Re・Im比が気に入らない.png

アスペクト比を設定する、


ax.set_aspect('equal')

は、x:y:z全てを、1:1:1にする。Nが大きい場合、64:1:1とかになるから、問題がある。
だから、

  • 複素平面を±1〜2の範囲で描く。
  • k軸を、その1.5〜2倍の大きさで描く。

事を目指した。
2Dプロットの場合は、

ax.set_aspect()

の引数に、数値を入れれば指定できた。数値は一個しか取れない。x:yの比なので、一個でも表現できる。
3Dの場合にも、同じメソッドは存在する。なぜかこちらも数値は一個しか取れない。x:y:zの比は、一個では表現でき…る?無理だと思うんだけど…

  • aspectメソッド自体はあっても、2Dプロット用のためか、x,yの二つしか設定できなかった。
  • x,y,zのバランスが整えられない。

という結論で、アスペクトをいじるための、他の。方法がほしくなった。

aspectの解決策

ここを参考にして、matplotlibのaxes3dのaspectを調節した

ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([1.2, 0.5, 1.5, 1]))

このコマンドだけで、下の図のように、Re・Im(縦・横)比は、なんとなく解決できた。

上記方法だと、プロット全体が、画面の中心からずれる。

しかし、余白が目立つ。どうにもシフトしてしまうようだ。

get_projで生まれる余白.png
こんな感じになる。
いや、もともと、この位の隙間が最初からあったんだっけ?
とにかく、余白が気に入らない。

あ、コレ、アフィン行列だ。

アスペクトを直すときに使ったコマンドをもう一度載せる。

ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([1.2, 0.5, 1.5, 1]))

get_projには、Axes3D.get_proj(ax)と、アフィン行列の、行列の積、を計算する、lambda式を入れる。
ちなみに、上記の、

np.diag([1.2, 0.5, 1.5, 1])

の結果は、

>>> np.diag([1.2, 0.5, 1.5, 1])
array([[ 1.2, 0. , 0. , 0. ],
[ 0. , 0.5, 0. , 0. ],
[ 0. , 0. , 1.5, 0. ],
[ 0. , 0. , 0. , 1. ]])

$$\rm aff= \left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$

この行列は、アフィン変換の拡大縮小行列ですな。この行列と、図中の座標の、行列の積(np.dotsで計算している部分。行列の積)を計算する関数(lambda式)が、get_projに入るという流れ。

アフィン変換のwikipediaは、難しくて読めないけど、「回転、拡大・縮小、平行移動、ミラー、せん断行列の事が、アフィン変換」というのが、自分の認識。多分、それ以外にも使うことがあるから、wikiでは、ああなるんだと思う。フーリエ変換も、そのせいで意味不明だし。まぁ、wikipediaは、あくまでも百科事典で、教科書じゃないから。

拡大縮小の行列は、3Dなら、

$$\left[ \begin{array}{ccc} x'\\\\ y' \\\\ z' \\\\ 1 \end{array}\right] = \left[ \begin{array}{ccc}a&0&0&0 \\\\ 0&b&0&0 \\\\ 0&0&c&0 \\\\ 0&0&0&1 \end{array}\right]\left[ \begin{array}{ccc}x\\\\ y \\\\ z \\\\ 1 \end{array}\right]$$

なはず。
3Dなのに、4D用意しているのは、平行移動行列の時とか、

$$\left[ \begin{array}{ccc} x'\\\\ y' \\\\ z' \\\\ 1\end{array}\right] = \left[ \begin{array}{ccc}1&0&0&d \\\\ 0&1&0&h \\\\ 0&0&1&l \\\\ 0&0&0&1 \end{array}\right]\left[ \begin{array}{ccc}x\\\\ y \\\\ z \\\\ 1 \end{array}\right]$$

だから、z軸動かしたい時は、3行目の4列目がないと困る。などがある。拡大・縮小や、回転などは、3*3で足りる変換もあるが、4*4が必要な他の変換等を、複数組み合わせて使うときに、同じサイズの行列だと、あらかじめかけ算して、ひとまとめにしてから計算することが可能だから、4*4にする。

  • そこは、マークしてるんだおれは。その内やろうとは、思っているんだ。
  • 円周上の点の座標を、回転行列に入れて、計算とかしたんだ。
  • その時、アフィン行列という分野を知ったんだ。
  • だから、アフィン行列のLaTeXは、2D 用から3D用まで、全部用意しているんだ。
  • あとは、フーリエへの動画を作り終わるだけなんだ。
  • そしたら、アフィン行列のアニメを作るんだ。
  • その後、レンダリング方程式のアニメも作るんだ。

んだんだ。

行列のかけ算、アフィン行列。この辺は、フーリエ変換とも直接関係する部分なので、存じておりました。
フーリエ変換は、フーリエ変換行列と、データの、行列のかけ算の計算だから。

np.diagは、対角項を指定した、配列(行列)を作る。

np.diagは、対角項というか、対角行列。
斜めのやつですな。
英語なら、diagonal matrixとか、この場合は、diagonal arrayとか。

当然、正方行列。
wikipediaに、そう載ってた

理由はともあれ、解決策

縦横比の指定がアフィンの拡大行列を使って行われていたので、
平行移動も、アフィンで行うことにする。

$$\left[ \begin{array}{ccc} x'\\\\ y' \\\\ z' \\\\ 1\end{array}\right] = \left[ \begin{array}{ccc}1&0&0&d \\\\ 0&1&0&h \\\\ 0&0&1&l \\\\ 0&0&0&1 \end{array}\right]\left[ \begin{array}{ccc}x\\\\ y \\\\ z \\\\ 1 \end{array}\right]$$

アフィン同士は、どう処理すればいいんだっけか。

3D回転の時は、x軸中心に回すやつと、y軸中心に回すやつの、行列の積を出して、使った覚えがある。



去年の年末、せっせと作っていた。

せっかくなので、アップして貼っておく。
しばらくしたら、もう少し、追加の情報を入れて、アップしなおす。

動画では、リングをくるくる回すために…

  1. リングを作るために、各点のx,y座標を、(cosθ, sin θ) 0<θ<2*πで作って、
  2. 回転行列(変数θが、for構文で動く)との行列のかけ算を計算して、回転後のx,y座標を作って、
  3. 計算結果を3Dプロットする、

という作戦で作った。

最後の、グチャーとしたのが、二軸回転のトラジェクトリ。
これは、二つの回転行列の、行列のかけ算で出した。

二軸の回転角θ、φは、別々に設定した。

上記は、たまたま、同種のアフィン行列(y,z,軸の回転行列)二つの組み合わせだった。

別種類の行列同士、(例えば、今回の”回転行列”と、”平行移動”)でも、行列のかけ算をするべきだろうか?

でも、めんどくせえ。
この場合、単純に行列の和でいいのかな?そっちなら、簡単だし、そっちであってほしい。
というか、インデキシングした配列に代入すればいっか…な?
どうでしょう。
「どのみち結果は、一緒」って事も、ありそう。

両方試せばいっか!

結局は、lambda式の中の、行列のかけ算の右側の引数を、うまい具合に設定できれば、なんだっていい。

複数のアフィン変換をする場合、それらの行列同士の積をするべきだろうが、

  • 代入
  • アフィン同士の積

の両方を試した。

代入で試してみた。

$$\left[ \begin{array}{ccc} x'\\\\ y' \\\\ z' \\\\ 1\end{array}\right] = \left[ \begin{array}{ccc}1.2&0&0&\fbox{-0.1} \\\\ 0&0.5&0&\fbox{0.1} \\\\ 0&0&1.5&\fbox{0} \\\\ 0&0&0&1 \end{array}\right]\left[ \begin{array}{ccc}x\\\\ y \\\\ z \\\\ 1 \end{array}\right]$$

四角で囲ったセルに、移動させたい数値を代入した。python的には下の様になった。

aff = np.diag([1.2, 0.5, 1.5, 1])
aff[0][3] = -0.1#x軸担当
aff[1][3] = 0.1#y軸担当
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), aff)

ん?動かない。

aff_shift_x_-0p1.png


極端に大きなな数字いれてやれ。

aff[0][3] = -30

aff_shift_x_-30y_0.png

あ、動いてますね。
わーい。

x,y,z3軸いじれるので、書き直すけど、

aff = np.diag([1.2, 0.5, 1.5, 1])
aff[0][3] = 0#x軸担当
aff[1][3] = 0#y軸担当
aff[2][3] = 0#z軸担当
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), aff)

こういう事ですわ。

  • xの場合
コードの変えた部分行列結果
なし$$\left[ \begin{array}{ccc}1.2&0&0&\fbox{0} \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$aff_shift_x_0y_0.png
aff[0][3] = -30#x軸担当$$\left[ \begin{array}{ccc}1.2&0&0&\fbox{-30} \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$aff_shift_x_-30y_0.png
aff[0][3] = -60#x軸担当$$\left[ \begin{array}{ccc}1.2&0&0&\fbox{-60} \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$aff_shift_x_-60y_0.png
  • yの場合
コードの変えた部分行列結果
なし$$\left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&\fbox{0} \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$aff_shift_x_0y_0.png
aff[1][3] = 2#y軸担当$$\left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&\fbox{2} \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$aff_shift_x_0y_2.png
aff[1][3] = 5#y軸担当$$\left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&\fbox{5} \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$aff_shift_x_0y_5.png
  • zの場合
コードの変えた部分行列結果
なし$$\left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&\fbox{0} \\\\ 0&0&0&1 \end{array}\right]$$aff_shift_x_0y_0.png
aff[2][3] = 2#z軸担当$$\left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&\fbox{2} \\\\ 0&0&0&1 \end{array}\right]$$aff_shift_x_-0y_0z_2.png
aff[2][3] = 5#z軸担当$$\left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&\fbox{5} \\\\ 0&0&0&1 \end{array}\right]$$aff_shift_x_-0y_0z_5.png

行列のかけ算は、代入と答えが違う…訳ないだろう。

「もう、動いてくれたし、別に、やらんでもいいかなぁ〜」とか思っちゃったけど、やる。

# aff1拡大・縮小行列
aff1 = np.diag([1.2, 0.5, 1.5, 1])

# aff2平行移動行列
aff2=np.diag([1., 1., 1., 1.])
aff2[0][3] = -20.0
aff2[1][3] = 0.5

$$\left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]\cdot\left[ \begin{array}{ccc}1&0&0&-20 \\\\ 0&1&0&0.5 \\\\ 0&0&1&0 \\\\ 0&0&0&1 \end{array}\right]$$

>>> np.dot(aff1, aff2)
array([[ 1.2 , 0. , 0. , -24. ],
[ 0. , 0.5 , 0. , 0.25],
[ 0. , 0. , 1.5 , 0. ],
[ 0. , 0. , 0. , 1. ]])

!?、代入と、微妙に答えが違うぜ。

いや、行列のかけ算は、順番が大事なんだぜ。
行列の掛け算では、左右入れ替えると、

  • 答えが変わる
  • そもそも、計算できなくなる。

とか、起こり得る。
今回は、正方行列なので、左右を入れ替えても、計算はできる。
しかし、結果は変わる。

>>> np.dot(aff2,aff)
array([[ 1.2, 0. , 0. , -20. ],
[ 0. , 0.5, 0. , 0.5],
[ 0. , 0. , 1.5, 5. ],
[ 0. , 0. , 0. , 1. ]])
もどった。というか、代入した場合と、同じになった。
逆の場合も、間違いではないんだろう。処理の順番によって、結果が変わるだけ。
下の表に、行列のかけ算をまとめた。
左側右側答え
$$\left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$$$\left[ \begin{array}{ccc}1&0&0&-20 \\\\ 0&1&0&0.5 \\\\ 0&0&1&0 \\\\ 0&0&0&1 \end{array}\right]$$$$\left[ \begin{array}{ccc} 1.2&0&0&-24 \\\\ 0&0.5&0&0.25 \\\\ 0&0&1.5&0 \\\\ 0&0&0&1  \end{array}\right]$$
$$\left[ \begin{array}{ccc}1&0&0&-20 \\\\ 0&1&0&0.5 \\\\ 0&0&1&0 \\\\ 0&0&0&1 \end{array}\right]$$$$\left[ \begin{array}{ccc}1.2&0&0&0 \\\\ 0&0.5&0&0 \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$$$\left[ \begin{array}{ccc} 1.2&0&0&-20 \\\\ 0&0.5&0&0.5 \\\\ 0&0&1.5&0 \\\\ 0&0&0&1 \end{array}\right]$$

この順番が、どこから来るのか分からないけど、順番注意なようですな。
拡大縮小→平行移動
平行移動→拡大縮小
では、意味が違うようです。

表の、下側の結果は、代入したのと、同じになっているし、代入した結果で事足りてる。

自分の中の結論としては、

  • 代入の方が、行数少ない。
  • 複数組み合わせて難しいことするなら行列のかけ算使う。しかし順番注意。

アフィンで、軸を動かせれば、何かしら使えそう

アフィンの部分を利用すれば、

  • ゆらゆら軸を揺らしながら、プロットしたり、
  • どっくんどっくん軸を脈動させながら、プロットしたり、

特殊なアニメーションができそう。動かない方が見やすいけど。
どういうシーンで、それを使うのかは、別にして…。
これからも、是非、是非、無駄なことをやっていきたいと思う。

ん…?結局グラフは、完成させないまま終わってしまった。凄い!8000文字近く書いた。

posted by yuchan at 07:00 | Comment(0) | python