Remrinのpython攻略日記

python3に入門しました。python3についてあれこれとサンプルコードとか。

matplotlibで図形

matplotlibで図形を描く方法。
 
plot()関数が呼ばれると背後でfigure()が呼ばれ、続いてaxes()が呼ばれてfigureの中に軸をつくる。
plot()関数を呼ばずにfigure()やaxes()で図、軸を設定できる。
 

import matplotlib.pyplot as plt

c1 = plt.Circle((0, 0), radius=1, fc="yellow", ec="r")
ax = plt.gca()
ax.add_patch(c1)
plt.axis("scaled")
plt.show()

 
f:id:rare_Remrin:20170506162014p:plain
 
○figure()関数
Figureオブジェクトを作る。
Definition :

figure(num=None,           # autoincrement if None, else integer from 1-N 
       figsize=None,       # defaults to rc figure.figsize 
       dpi=None,           # defaults to rc figure.dpi 
       facecolor=None,     # defaults to rc figure.facecolor 
       edgecolor=None,     # defaults to rc figure.edgecolor 
       frameon=True, 
       FigureClass=Figure, 
       **kwargs)

 

num figure番号。整数または文字列のオプション引数でデフォルトはNone。もしnumが与えられなければ、新しいfigureを作り、figure番号を1増やす。figureオブジェクトはこの番号をnumber属性として保持する。もしnumが与えられ、すでに存在しているfigure番号だった場合は、そのfigureオブジェクトをアクティブにし、参照を返す。存在しないfigure番号のときは、新規のfigureオブジェクトを作成し、その参照を返す。numが文字列のときはウィンドウのタイトルがnumになる。
figsize (幅, 高さ)の整数のタプルで単位はインチ。デフォルトはrc figure.figsize
dpi figureの解像度。整数で、デフォルトははrc figure.dpi
facecolor 背景色。デフォルトはrc figure.facecolor
edgecolor 枠の色。デフォルトはrc figure.edgecolor

 

返り値 figureオブジェクト

 
○axes()関数
 Figureオブジェクトに座標軸を付け加える

parameter 内容
facecolor color 軸の背景色
frameon True or False 枠を表示するか
sharex otherax 他の軸とxaxis属性を共有するか
sharey otherax 他の軸とyaxis属性を共有するか
polar True, False 極座標を使うか
aspect "equal", "auto", num アスペクト比x/y。"equal"は1:1

 
○axis()メソッド
素数4のタプルを引数としてaxis()メソッドを呼ぶと、座標軸の範囲を変更できる。

引数 内容
なし 現在の座標軸の範囲を(xmin, xmax, ymin, ymax)のタプルで返す。
"off" 座標軸を表示しない
"equal" 円が円に見える。
"scaled" 座上軸の両端の自動調整
"tight" 余白を小さく?
"image" データの範囲に合わせる
"auto" 非推奨
"normal" 非推奨
"square" x軸の範囲(xmax-xmin)とy軸の範囲を等しくする

 

fig = plt.figure()
ax =plt.axes()
print(plt.axis())   # (0.0, 1.0, 0.0, 1.0)

v = (0, 2, 0, 3)
plt.axis(v)
plt.show()

 
x軸の範囲が0~2、y軸の範囲が0~3になっている。
f:id:rare_Remrin:20170506155223p:plain

gcf()関数:現在のFifureへの参照。オブジェクトがない場合は作成する。
gca()関数:現在のAxesへの参照。オブジェクトがない場合は作成する。
 
○ax.set_aspect()関数
 グラフのアスペクト比を設定する。

○円plt.Circle以外にもいろいろな図形が書ける。
 Ellipse, Polygon, Rectangleなどなど
 
参考:
patches — Matplotlib 2.0.1 documentation

csvライブラリ

csvモジュールの使い方について。
 
以下の内容のtest.csvを準備しました。

1,20
2,30
3,40
4,50

 
csvファイルの読み取り
(1)各行はリストとして読み込まれる。

import csv

with open("test.csv", "r") as f:
    data = csv.reader(f)
    print(data)  <_csv.reader object at 0x...>
    for i in data:  # 各行がリストになっている
        print(i)
# ['1', '20']
# ['2', '30']
# ['3', '40']
# ['4', '50']

 
(2)joinで見やすく。

with open("test.csv", "r") as f:
    data = csv.reader(f)
    print(data)
    for i in data:
        print(", ".join(i))
# 1, 20
# 2, 30
# 3, 40
# 4, 50

csv.reader()の引数になるのは、ファイルオブジェクトまたはリスト
 
(3)読み飛ばしたい行があるときはnext()する。

with open("test.csv", "r") as f:
    data = csv.reader(f)
    next(data)                    # 1行読み飛ばす
    for i in data:
        print(", ".join(i))       # 2, 30
                                  # 3, 40
                                  # 4, 50  

 
(4)読み飛ばす行をヘッダーなどとして使用するときは代入をしておく。

with open("test.csv", "r") as f:
    data = csv.reader(f)
    header = next(data)        # 1行読み飛ばし、headerに代入
    print(header)              # ['1', '20']
    for i in data:
        print(", ".join(i))    # 2, 30
                               # 3, 40
                               # 4, 50  

 
csvファイルの書き込み
newline=""とオプションを入れないと、空白行が追加されてしまう。
(1)

import csv
with open('test.csv', 'w', newline="") as f:
    writer = csv.writer(f)
    writer.writerow([1] + [20])
    writer.writerow([2, 30])
    for i in range(3, 5):
        writer.writerow([i, (i + 1) * 10])

 
(2)

data =[[1, 20], 
       [2, 30], 
       [3, 40],
       [4, 50]]
with open('test.csv', 'w', newline="") as f:
    writer = csv.writer(f)
    writer.writerows(data)

 
参考:
14.1. csv — CSV ファイルの読み書き — Python 3.6.1 ドキュメント

matplotlibの色名一覧

matplotlibの色名一覧
 
f:id:rare_Remrin:20170505171728p:plain
 

この色名一覧はpythonで書かれていて、同じページにコードも載っています。
あとでゆっくりコードを読んでみようと思います。
 

# coding: utf-8

from __future__ import division

import matplotlib.pyplot as plt
from matplotlib import colors as mcolors

colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)

# (HSV形式,色名)というタプルに直し、Hをキーにしてソートしたリストにする
by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgba(color)[:3])), name)
                for name, color in colors.items())
# sortしたタプルのリストから、色名だけを抜き出したリストを作る
sorted_names = [name for hsv, name in by_hsv]


n = len(sorted_names)   # 8色 + 148色 = 全156色
ncols = 4               # 列数を4にする。他の数でもよい
nrows = n // ncols + 1  # 行数を計算

fig, ax = plt.subplots(figsize=(8, 5)) # 横8インチ、縦5インチ

# Get height and width
X, Y = fig.get_dpi() * fig.get_size_inches()
h = Y / (nrows + 1)   # 1マスの縦のドット数
w = X / ncols         # 1マスの横のドット数

for i, name in enumerate(sorted_names):
    col = i % ncols            # 0番 1番 2番 3番
    row = i // ncols           # 4番 5番 6番 7番 ...のように割り振る
    y = Y - (row * h) - h      # 表示するy座標

    xi_line = w * (col + 0.05) # 長方形の左端のx座標
    xf_line = w * (col + 0.25) # 長方形の右端のx座標
    xi_text = w * (col + 0.3)  # 色名の文字列を表示するx座標

    # 左よせで文字を表示
    ax.text(xi_text, y, name, fontsize=(h * 0.8),
            horizontalalignment='left',
            verticalalignment='center')
    # hlines(横向きの直線)として色つきの長方形を表示
    ax.hlines(y + h * 0.1, xi_line, xf_line,
              color=colors[name], linewidth=(h * 0.6))

ax.set_xlim(0, X)
ax.set_ylim(0, Y)
ax.set_axis_off()

fig.subplots_adjust(left=0, right=1,
                    top=1, bottom=0,
                    hspace=0, wspace=0)
plt.show()

わからないことが多すぎるので、いろいろ確認してみた。
・mcolors.BASE_COLORS : 
 8つの基本色。アルファベット1文字で表す。
 色名:RGBの辞書型になっている。 

print(mcolors.BASE_COLORS)
# {'y': (0.75, 0.75, 0), 'm': (0.75, 0, 0.75), 'r': (1, 0, 0), 
#  'c': (0, 0.75, 0.75), 'k': (0, 0, 0), 'g': (0, 0.5, 0), 
#  'w': (1, 1, 1), 'b': (0, 0, 1)}

 
・mcolors.CSS4_COLORS
 148色の(色名:16進コード)の辞書型

print(len(mcolors.CSS4_COLORS))
# 148

print(mcolors.CSS4_COLORS)
# {'oldlace': '#FDF5E6', 'limegreen': '#32CD32', 'palevioletred': '#DB7093',
# 'mintcream': '#F5FFFA', 'rosybrown': '#BC8F8F', 'blueviolet': '#8A2BE2', 
# 'lime': '#00FF00', 'orange': '#FFA500', 'mediumorchid': '#BA55D3', ...

 
参考:
color example code: named_colors.py — Matplotlib 2.0.1 documentation

matplotlibで散布図

matplotlibで散布図を描く方法について。
 
Definition :

plt.scatter(
        x, y,                 # x座標, y座標の配列
        s=None,               # マーカーのサイズ
        c=None,               # マーカーの色
        marker=None,          # マーカーの形
        cmap=None,            # cがfloat型のときのカラーマップ
        norm=None,            # cをfloat型配列にした時の規格化インスタンス指定
        vmin=None, vmax=None, # 最大・最小
        alpha=None,           # 透明度
        linewidths=None,      # 線の太さ
        verts=None, 
        edgecolors=None,      # 線の色
        hold=None, 
        data=None, 
        **kwargs)

色名一覧はここ
 
 
・乱数で100個の点を描く

import numpy as np
import matplotlib.pyplot as plt

x = np.random.rand(100)
y = np.random.rand(100)
plt.scatter(x, y)
plt.show()

 
f:id:rare_Remrin:20170505153640p:plain
  
・マーカーをひし形にして、色を付ける。

x = np.random.rand(100)
y = np.random.rand(100)
plt.scatter(x, y, s=100, c="orange", edgecolor="red")
plt.show()

 
f:id:rare_Remrin:20170505153737p:plain 
 
・直線、軸、タイトルつき。

import numpy as np
import matplotlib.pyplot as plt
#日本語を使う場合は以下の2行でフォントを準備
from matplotlib.font_manager import FontProperties
fp = FontProperties(fname='C:\WINDOWS\Fonts\msgothic.ttc', size=14)

x = np.arange(20)
y = x + np.random.rand(20)*4 
plt.scatter(x, y, s=50, marker="x", c="red", linewidth=1)
plt.plot([0, 19], [2, 21], c="blue")  #直線の描画
plt.title("散布図", fontproperties=fp)
plt.xlabel("x軸", fontproperties=fp)
plt.ylabel("y軸", fontproperties=fp)
plt.show()

 
f:id:rare_Remrin:20170505153934p:plain 
 

chars = "^<>vo+d"
x = np.random.rand(100)
y = np.random.rand(100)
markers = [chars[i%7] for i in range(100)]
for i in range(100):
    plt.scatter(x[i], y[i], marker=markers[i])
plt.show()

f:id:rare_Remrin:20170526175130p:plain
 

x = np.random.rand(10000)
y = np.random.randn(10000)
plt.scatter(x, y, alpha=0.2)
plt.show()

f:id:rare_Remrin:20170526175210p:plain
 

x = np.random.randn(10000)
y = np.random.randn(10000)
plt.scatter(x, y, alpha=0.2)
plt.show()

f:id:rare_Remrin:20170526175252p:plain
 

カラーマップを使う。

x = np.random.rand(100)
y = np.random.rand(100)
col = np.linspace(0, 1, 100)
plt.scatter(x, y, c=col, cmap="spring")
plt.colorbar()  #カラーバーの表示
plt.show()

 
f:id:rare_Remrin:20170505154033p:plain 
 
カラーマップの一覧はここに。
 
参考:

Python でデータサイエンス

エラー

構文解釈時のよくあるエラーについて。
 
AttributeError:
・'~' object has no attribute '~'
  属性名、メソッド名などが間違っている。存在しないのに呼び出した。
  lとrや大文字小文字などスペルミスを見直す。
  ドットを打ち込んで属性の候補一覧からTAB補完で選ぶとミスが減る。
  属性の候補一覧に載っていない場合は変数などの型がミスっているかも。
 
NameError 変数名などが間違っている。 
・name '~' is not defined
  代入(assign)前の変数を利用しようとした。
  変数をdelしてしまった。
  変数名のタイプミスなど。
 
IndentationError:
・expected an indented block
  必要な部分にインデントされていない。
  def, if, for, while, classなどの次の行。
・unexpected indent
  インデントしてはいけない部分でインデントしてしまった。
 
IndexError インデックスミス
・list index out of range
  インデックスの範囲外を指定してしまった。
  print(len(list1))などで要素数を確認する。
  最初の要素は[1]ではなく[0]
 
TypeError  型が合ってない
・'list' object is not callable
  リストオブジェクトを関数として扱ってしまっている。
  ・list1[0]とするのをlist1(0)としている。
  ・何らかの関数をリストで上書きしてしまっている。
   list=[1, 2, 3]などと、組み込みの関数名を変数名にするとミスが多い。
・'int' object is not callable
  intオブジェクトを関数として扱ってしまった。
  数学の式で(x + y)(x - y)はpythonでは(x + y)*(x - y)
 
SyntaxError
・invalid character in identifier
  全角スペース、タブなどが混入。webのコピペでよくある。
・invalid syntax
  その他のエラー。
  エラーよりも前の行で()の対応が合っていない。とくにclosing parenthesis「)」
・EOL while scanning string literal
  文字列のクオーテーションが閉じられていない。
・Missing parentheses in call to 'print'
  print文は()を付ける。
・unexpected EOF while parsing
  構文解釈中にプログラムの最後まで読んでしまった。
  最終行付近で閉じ括弧をわすれていたり。

pythonで統計

pythonを使って統計処理をします。
 
○1変数の基本統計量
素数
平均mean
範囲
最頻値mode
中央値median
分散variance
標準偏差standard deviation

# -*- coding: utf-8 -*-

from collections import Counter

def mean(seq):
    return sum(seq) / len(seq)    # 平均=合計/要素数

def range_(seq):
    return min(seq), max(seq)     # 範囲:(最小値, 最大値)

def mode(seq):
    c =Counter(seq).most_common() # (要素, 頻度)というタプルの頻度順リスト
    max_f = c[0][1]               # c[0]:最頻出要素
    modes = filter(lambda x:x[1]==max_f, c) # 頻度1位を全て抜き出す
    return [i[0] for i in modes]  # 値のみをリスト内包表記にして返す
             
def median(seq):
    s = sorted(seq)
    if len(s) % 2 == 0:          # 要素が偶数個なら2つの平均
        return (s[len(s) // 2] + s[(len(s) // 2) - 1]) / 2
    else:
        return s[len(s) // 2]    # 要素が奇数個なら中央の値

def sigma(seq):                  # 分散
    m = mean(seq)
    total = sum(map(lambda x:(x - m)**2, seq))
    return total / len(seq)

def sd(seq):
    return sigma(seq) ** 0.5     # 標準偏差=sqrt(分散)

def stat(seq):
    if len(seq) == 0:
        print("要素がありません")
    else:
        print("要素数:", len(seq))
        print("平均:", round(mean(seq), 2)) # 小数第2位までで表示
        print("範囲:", range_(seq))
        print("最頻値:", *mode(seq))
        print("中央値:", median(seq))    
        print("分散:", round(sigma(seq), 2))
        print("標準偏差:", round(sd(seq), 2))

list1 = [100, 60, 50, 70, 100]  # タプルでもOK
stat(list1)

# 要素数: 5
# 平均: 76.0
# 範囲: (50, 100)
# 最頻値: 100
# 中央値: 70
# 分散: 424.0
# 標準偏差: 20.59

 
○2変数の基本統計量
相関係数
公式:f:id:rare_Remrin:20170505165448p:plain

# correlation coefficient
def corr(x, y):
    if len(x) != len(y):
        return "different length"
    sx = sum(x)
    sy = sum(y)
    sx2 = sum(map(lambda x:x**2, x))
    sy2 = sum(map(lambda x:x**2, y))
    sxy = sum(map((lambda x, y:x*y), x, y))
    n = len(x)
    try:
        return (n*sxy-sx*sy)/((n*sx2-sx**2)*(n*sy2-sy**2))**0.5
    except:
        return 0
    
x = [1, 2, 3]
y = [5, 5, 6]
print("相関係数:", corr(x, y)) # 相関係数: 0.8660254037844387

 
pythonでの乱数についてはこちら

pythonで円周率を求める

pythonで円周率を求めるアルゴリズム
 
ライプニッツの方法
収束が遅い。
100万回計算して、6桁くらい。
f:id:rare_Remrin:20170504163602p:plain

pi4 = 0
for i in range(1000000):
    pi4 += (1 / (i * 4 + 1) - 1 / (i * 4 + 3))
print(pi4 * 4)    # 3.141592153589902

 
○区分求積を使う。
収束が速い。
25回の計算で15桁くらいまで出ます。
f:id:rare_Remrin:20170504163612p:plain

pi4 = 0
for i in range(1, 50, 2):
    pi4 += (-1)**(i//2) * (1/2**i + 1/3**i) / i
print(pi4 * 4)        # 3.1415926535897922

 
ガウスルジャンドルの方法
収束が非常に速く、4回も計算すれば15桁まで出ます。

a, b, t, p = 1, 1/2**0.5, 1/4, 1
for i in range(4):
    a, b, t, p = (a+b)/2, (a*b)**0.5, t-p*((b-a)/2)**2, p*2 
    print((a + b)**2 / (4 * t))

# 3.1405792505221686
# 3.141592646213543
# 3.141592653589794
# 3.141592653589794

 
モンテカルロ法
乱数を使って円周率を求める方法。
原点中心、半径1の円とその円に外接する正方形の面積比がπ/4であることを利用し、1万個の座標を乱数で生成して円周率を求めてみた。
サンプルコード(1) randomモジュールで。

import random

inner = 0
for i in range(10000):
    x, y = random.random(), random.random()
    if x**2 + y**2 < 1:
        inner += 1
print(inner *4 / 10000)   # 3.1446

 
サンプルコード(2) NumPyのrandomで。

np.random.seed(1)               # 乱数の初期化。好みの数で。
arr = np.random.rand(2, 10000)  # 2行10000列の乱数ndarray
distance = (arr**2).sum(axis=0) # 全要素を2乗し縦に合計すると原点からの距離
count = (distance < 1).sum()    # 距離が1未満か判断しTrueの個数を調べる
print(4 * count / 10000)        # 3.1348

 
1万個の散布図を描くとキャンバスが埋まってしまうので、1000個で描画
f:id:rare_Remrin:20170509174416p:plain
 

import numpy as np
import matplotlib.pyplot as plt

c1 = plt.Circle((0, 0), radius=1, fc="None", ec="r", linewidth=3)
ax = plt.gca()
ax.add_patch(c1)

plt.axis("scaled")
plt.xlim(0, 1)
plt.ylim(0, 1)

x = np.random.rand(1000)
y = np.random.rand(1000)
plt.scatter(x, y, marker="x")
plt.show()

 
○逆三角関数を使う方法
cos(π)=-1なので
π=arccos(-1)

import math
print(math.acos(-1)) # 3.141592653589793

 
参考:
円周率を計算してみよう - 金沢工業大学
Wikipedia ガウス・ルジャンドルのアルゴリズム