Python3 – リストの初期化

空のリストの生成

空の場合は添字でアクセスできない。

要素が1個のリストの生成

要素が複数個のリストの生成

最も一般的な、要素をリテラルで指定したリスト。

終わりに”,”をつけても同じ。

要素がない場合はエラー。

多次元のリスト

多次元配列のように定義可能。後ろの方の添字が入れ子の内側のリストの添字。

異なる要素を持つリスト

異なるタイプの要素を持つことができる。

繰り返し指定

*演算子

*演算子で繰り返し数を指定。要素の掛け算ではなく、リストの掛け算のように書く。

これは+演算子によるリストの結合だと考えるとよい。

内包表記

要素が固定の場合。

要素を変化させる場合。

*演算子を使う場合の注意点

*演算子は、同一インスタンスへの参照を複製するだけなので、意図した結果にならない場合がある。

[0, 0]が3つ複製されるが、これらは同じインスタンスを指すため、一つの要素の変更が他にも反映されてしまう。

内包表記なら毎回インスタンスが生成されるので、意図した結果になる。

 

単回帰分析~コンビニエンスストア

概要

Pythonを使った統計計算と図示の練習のため、コンビニエンスストアで単回帰分析をやってみた。

コンビニの店舗数は「商業動態統計年報」の2016年データを使い、説明変数として2015年の国勢調査人口、2018年の国土地理院による都道府県面積、2017年道路統計年報の道路実延長データを使った。

計算コードは以下の通り。

人口との関係

コンビニ店舗数と人口の散布図と回帰式を以下に示す。

相関係数が極めて高いのは当然で、やはりコンビニの出店計画には人口ファクターが強く作用していることがわかる。

定数項bが店舗数のオーダーに比べてほぼゼロというのも興味深い。

係数aの値からは5万人弱で1店舗ということになるが、人口10万くらいの都市で2店舗しかないことになり、ちょっと少ないような気がする。もしかすると、一定規模以上の市町村単位や都市単位くらいで層化して出店計画を立てているのかもしれない。

面積との関係

店舗数と面積の関係

次に店舗数と都道府県面積の関係を見てみた。

この結果はかなりはずれで、データを見ても人口が少なく面積が群を抜いて大きい北海道の影響を大きく受けている。

ここで、面積が極端に大きい北海道(83423.83㎢~1位、2906店舗~5位)と面積が小さいが集積度が極端に高い東京都(2193.96㎢~45位、7003店舗~1位)の2つを除いて計算してみる。

これはさらにおかしな結果で、面積が小さいほど店舗数が多いことになる。

考えてみれば、集客数を期待するなら人口が集積している地域が有利だから、人口密度に比例する可能性を考えた方がいいのかもしれない。もし面積が小さい県の方が集積度が高いと想定すると、面積だけを取り出したときに逆の関係になるとも考えられるが、相関係数や決定係数が小さすぎるので考察は難しい。

人口密度

以下は人口密度との関係。

今度はかなりきれいに相関の高さが出ている。

直接的な計算式に入れているかどうかわからないが、GISなどで出店計画を立てるとしたら、人口密度の高いエリアを選んでいくだろうことが想定される。

ただ、店舗数は人口などといった売り上げに直結するデータから導かれるのが普通で、人口密度が高くても人口が少なければ出店インセンティブにはならない。

人口と人口密度の関係

試しに人口を説明変数、人口密度を被説明変数として両者の関係を見てみると、驚くことに「人口が多いほど人口密度が高くなる(あるいはその逆)」という関係になる。

ここから先は人口論や地域論になりそうなので置いておくが、少なくとも日本においては、「狭いところほど人が集まっている傾向がある」ということになりそうである。

もちろんこれは他の国でも一般に当てはまることかもしれないが、朝のラッシュ時に特定の車両に無理やり乗り込んでいる割に離れた車両がすいているとか、1本電車を遅らせたらガラガラだったとか、そのあたりの行動パターンを見ていると、何となく日本に特有のような気がする。

道路延長との関係

最後に道路延長との関係を見てみる。

東京のように稠密な都市は例外とすると、概ね関係はありそうである。ただし相関係数、決定係数は高くない。

コンビニ店舗が道路の利便性に依っていることは推測できるが、やはり人口という売り上げ直結のデータに比べると関係は弱い。

高速道路の延長についても見てみたが、こちらはほとんど関係は見られなかった。

ただ、高速道路の延伸に伴ってコンビニエンスストアの店舗数が伸びているようであり、マクロな延長というよりも物流上のインパクトが大きいことは予想される。

 

Python3 – 浮動小数点の誤差

概要

Pythonで浮動小数点誤差に行き当たった。

コンソール上にはErrorが表示され計算結果はnanになるが、配列を使っていたので、問題がどこで生じているかわかるまでにちょっと手間取った。

解析上の考え

中心が(cx, cy)、半径がrの円を考え、x座標を与えてそれに対する円上のy座標を計算しようとしていたとき。

このような円の方程式は、陰関数では以下のようになる。

     \begin{equation*} (x - c_x)^2 + (y - c_y)^2 = r^2 \end{equation*}

これをy ≥ 0で解くと、

     \begin{equation*} y = c_y + \sqrt{r^2 - (x - c_x)^2} \end{equation*}

問題の発生

中心の座標が(0.5, 0.5)、半径が0.3となるような円が欲しかったので、xの定義域を0.3~0.8として次のようなコードをPythonで書いた。実際はy < 0の部分も必要で、円周上だけでなく内部の座標をランダムに計算し、それらの相関係数を計算するというものだったが、本質的には変わらない。

その結果、cor=nanとなってしまう。実行時にsqrtの計算で警告が出ている。

そこで配列x、yを表示させてみると、yの最後の要素がnanになっている。

さらに、sqrtの中を表示させてみる。

最後のところで差し引かれる方の値がごくわずかに大きくなっていて、その結果根号の中が負の値となってnanが発生したらしい。

解決策

linspaceの範囲を円周上の上限・下限から少し内側にすることも考えられるが、今回はnumpy.around()を使った。

numpy.around(a, decimals=0)

これは値aを小数点以下decimalsの桁数で丸めてくれるもので、今回は小数点以下10桁目で丸めてみた。

この結果、正常に計算。

注意点

  • 解析上値が等しくなる(差し引きゼロになる)ような場合でも、浮動小数点ではごくわずかな誤差のため、エラーとなることがある
  • numpy.linspace()を使うと上限・下限の間を等分してくれるのでありがたいが、上限・下限自体が他の値と厳密に等しくなければならない場合には、浮動小数点の誤差が生じるため注意が必要

 

Python3 – numpy

全般

numpyのインストール

randomモジュール

基本機能

配列・ndarray

Tips

配列の応用

集計・確率・統計

各種関数

where()関数

Numpy.where()は、配列中の要素を検索してそのインデックスを得たり、配列の条件によって指定した配列から要素を選び出すことができる。

 

numpy – 統計量と組み込み関数の注意点

概要

Pythonのnumpyで統計量を扱う場合、分散・共分散で注意を要する点がある。Excelの関数でも同様だが、分散・共分散が標本値からそのまま計算した値か、不偏推定量として計算した値か、ということを意識する必要がある。

ここでは、ndarrayから律義に計算した値と、numpyの組み込み関数から計算した値を比較してみる。

標本数・総和・平均

標本として与えた配列の要素数、全要素の和、総和を要素数で除した値で、そのまま母集団の不偏推定量。

分散・標準偏差の食い違い

var()std()は標本の分散・標準偏差

分散・共分散については、提供されたメソッドvar()std()は標本数で除した標本分散・共分散となる。

不偏分散やその平方根を求める場合は、var()std()の引数でddof=1とする。

cov()で与えられるのは不偏推定量

numpy.cov()で二つの一次元配列の分散・共分散行列を得ることができる。分散共分散行列は

     \begin{equation*} \left[ \begin{array}{ll} {\rm Var}(X) & {\rm Cov}(XY) \\ {\rm Cov}(XY) & {\rm Var}(Y) \end{array} \right] \end{equation*}

で定義されるが、以下の計算結果の対角要素は、先の分散の計算結果(2.0)と異なっている。

これらの値は、分散を標本分散ではなく不偏推定量で計算しているためで、試しに先の計算を不偏分散で計算してみると結果は整合する。

ちなみに標本分散は偏差の二乗和を標本数nで割り、不偏分散はn-1で割る。

共分散にも注意

上記では分散について確認したが、共分散についても注意が必要。

共分散は一般に以下で定義される。

     \begin{equation*} {\rm Cov}(X, Y) = E((X - \overline{X}) (Y - \overline{Y})) = \frac{1}{n} \sum_{i=1}^{n} (x_i - \overline{x}) (y_i - \overline{y}) \end{equation*}

これに従って計算した結果。

この結果は、numpy.cov()で計算した非対角要素2.5と食い違う。

ここでn→n-1として計算しなおすと結果は2.5となり同じ値となる。

共分散用は、たとえば回帰分析の残差変動を計算するときに利用するが、その時には標本の共分散を使うので、注意が必要。

numpyで統計計算をする際、特に共分散については、律義に定義式から計算するのがよさそう。

 

numpy – 配列の統計計算(二次元配列)

概要

二次元配列についても、一次元配列と同様な統計関係のメソッド群がある。

二次元についても、同じ機能のメソッドがnumpy、ndarrayのメソッドとして準備されている。

二次元配列の場合は、最小・総和・平均などの計算を全要素/列単位/行単位のいずれで行うかを区別する。具体例として、以下の3×3配列の最小値min()を全要素、列単位、行単位で計算した例を示す。

引数axisを指定しないと全要素が対象となり、axis=0を指定すると列ごとの計算結果を一次元の配列に、axis=1を指定すると行ごとの計算結果を一次元の配列にして返す。

axis=1の場合、計算は行ごとに行われるが、結果は列ベクトルではなく行ベクトルの配列となる。

また、numpyのメソッドではなくndarrayのメソッドでも同じように使える。

最小値・最大値

最小値/最大値を返す。

numpy.min(m, axis=0/1)
numpy.max(m, axis=0/1)
m.min(axis=0/1)
m.max(axis=0/1)

結果は同じなので、numpyのメソッドについて実行例を示す。

総和・総積

一次元配列の全要素の和・積を返す。

numpy.sum(m, axis=0/1)
numpy.prod(m, axis=0/1)
m.sum(axis=0/1)
m.prod(axis=0/1)

平均・分散・標準偏差

一次元配列の要素の平均、分散、標準偏差を返す。分散は標本分散なので、不偏分散が必要な場合はvar()*n/(n-1)とする(ただしn=len(v))。

numpy.mean(m, axis=0/1)
numpy.var(m, axis=0/1)
numpy.std(m, axis=0/1)
m.mean(axis=0/1)
m.var(axis=0/1)
m.std(axis=0/1)

累積和・累積積

一次元配列の要素について先頭から累積して積・和を計算し、それらを要素とする配列を返す。

二次元配列でaxis=0を指定した場合、列ごとに行方向に累積した値が並べられた二次元配列となり、axis=1を指定した場合は行ごとに列方向に累積した値が並べられた二次元配列となる。

numpy.cumsum(m)
numpy.cumprod(m)
m.cumsum()
m.cumprod()

 

numpy – 配列の統計計算(一次元配列)

概要

一次元配列について、様々な統計関係の計算をするメソッド群。

同じ機能のメソッドがnumpy、ndarrayのメソッドとして準備されている。例えば一次元配列vについて最小値を求めるメソッドは、numpy.min(v)v.min()のいずれも同じ結果を返す。

最小値・最大値

一次元配列の要素のうち最小値/最大値を返す。

numpy.min(v)
numpy.max(v)
v.min()
v.max()

結果は同じなので、numpyのメソッドについて実行例を示す。

総和・総積

一次元配列の全要素の和・積を返す。

numpy.sum(v)
numpy.prod(v)
v.sum()
v.prod()

平均・分散・標準偏差

一次元配列の要素の平均、分散、標準偏差を返す。分散は標本分散なので、不偏分散が必要な場合はvar()*n/(n-1)とする(ただしn=len(v))。

numpy.mean(v)
numpy.var(v)
numpy.std(v)
v.mean()
v.var()
v.std()

     \begin{eqnarray*} \overline{V} &=& \frac{1 + 2 + 3 + 4}{4} = 2.5 \\ {\rm Var}(V) &=& \frac{(1-2.5)^2+(2-2.5)^2+(3-2.5)^2+(4-2.5)^2}{4} \\ &=& \frac{2.25+0.25+0.25+2.25}{4} = \frac{5}{4} \\ &=& 1.25 \\ \sigma_V &=& \sqrt{1.25} = 1.1180 \ldots \end{eqnarray*}

累積和・累積積

一次元配列の要素について先頭から累積して積・和を計算し、それらを要素とする配列を返す。

numpy.cumsum(v)
numpy.cumprod(v)
v.cumsum()
v.cumprod()

 

numpy – 配列要素の演算

要素に対するスカラー演算

配列の要素に対するスカラー演算は、それぞれの要素に作用。

二次元配列も同じ。

なお、配列が後ろに来ても要素ごとに演算。

要素ごとの演算

同じ形状の配列同士の演算は、それぞれの要素ごとの演算。

要素ごとの比較

配列同士の比較は、要素ごとの比較結果の配列。

関数演算

numpyの関数の引数が配列の場合、要素ごとの演算結果となる。

 

ndarray – 配列の生成

リテラル

要素をリテラルで指定する。

2次元配列は行列形式できれいに表示されるが、3次元以上は1行表示になる。

リストでもタプルでも結果は同じ。

自動生成

配列形状を指定して全要素を特定の値で埋める

全要素が0、1の配列

numpy.zeros(shape, [dtype=None], [order='C'])
numpy.ones(shape, [dtype=None], [order='C'])

引数はどちらも同じで、配列形状、データタイプ、行列優先の指定。

多次元配列を生成する場合、配列のサイズをタプルとして引数に渡す。

全要素の数値を指定

numpy.full(shape, fill_value, [dtype=None], [order='C'])]

他の配列と同じ形状で特定の値を埋める

全要素が0、1の配列

numpy.zeros_like(a, [dtype=None], [order='C'])
numpy.ones_like(a, [dtype=None], [order='C'])

全要素の値を指定

numpy.full_like(a, fill_value, [dtype=None], [order='C'])]

 初期化せず配列枠だけ確保する

zeros()ones()と同じで、配列の形だけを確保するが各要素は初期化しない。

この場合も多次元配列ではサイズのタプルを引数で渡す。

配列形状を(1, 1)で指定すると、1要素の2次元配列が確保される。

配列形状を数値0で指定すると、空の配列が確保される。

配列形状で0の次元が含まれると空の配列になる。

内包表記

リストの内包表記が使える。

range()関数

普通にrange()関数も使える。

numpy.arange()~間隔を指定

numpy.arrange([start=0], stop, [step=1], [dtype=None])

numpy.arange()関数はrange()関数よりも高速な生成が可能。

range()関数と同じようにパラメータを設定。dtypeはデータ型を指定。

range()関数は整数しか引数に指定できないが、numpy.arange()関数はfloatの指定が可能。

numpy.linspace()~分割数を指定

numpy.linspace(start, stop, [num=50], [endpoint=True], [retstep=False], [dtype=None])

初期値と終了値を指定して、その間を等分割したリストを返す。分割数を指定しないとデフォルトで50分割。基本の型はfloat。

この分割数は区間数ではなく、生成される数列の個数である点に注意。以下の例では0~1の間で10個の数値を生成させるため、区間数は9となる(植木算の考え方)。

デフォルトでは終端値を含めるが、含めないようにもできる。

生成された数列と分割幅のタプルを返すことができる。

特定の行列の生成

numpy.identity()~単位行列

numpy.identity(n)

正方行列の1辺の要素数を指定して、単位行列を生成する。

numpy.tri()~下三角行列の生成

numpy.tri(n)

左下の要素が1の下三角行列を生成する。

numpy.tril()numpy.triu()~行列の三角化

numpy.tril(matrix)
nunmpy.triu(matrix)

与えられた行列を下三角/上三角化。