pandasの使い方(3) インデックス
pandasのインデックスオブジェクトについて。
行や列のインデックスは「インデックスオブジェクト」として保持。
行:DataFrame.index
列:DataFrame.columns
import numpy as np import pandas as pd data = {"year": [2010, 2011, 2012, 2013], "pref":["千葉", "山口", "岐阜", "東京"]} f = pd.DataFrame(data) print(f) # pref year # 0 千葉 2010 # 1 山口 2011 # 2 岐阜 2012 # 3 東京 2013 print(f.index) # RangeIndex(start=0, stop=4, step=1) print(f.columns) # Index(['pref', 'year'], dtype='object')
インデックスオブジェクトは固定長のsetのような動作。
print(0 in f.index) # True print(4 in f.index) # False
・インデックスオブジェクトのメソッド
append | インデックスオブジェクトを追加 |
diff | 集合の差 |
intersection | 集合の積(共通部分) |
union | 和集合 |
isin | 各要素が含まれているかのbool配列 |
delete | i番目の要素を削除 |
drop | 指定した値を削除 |
insert | 要素の挿入 |
is_monotonic | 各要素が1つ前の要素と等しいか大きいならTrue |
is_unique | 重複なしならTrue |
unique | 重複のない配列にする |
print(f.index) # RangeIndex(start=0, stop=4, step=1) ind = f.index.insert(2, 10) print(ind) # Int64Index([0, 1, 10, 2, 3], dtype='int64') print(f.index.isin([1, 3, 10])) # [False True False True] f2 = f.reindex(ind) print(f2) # pref year # 0 千葉 2010.0 # 1 山口 2011.0 # 10 NaN NaN # 2 岐阜 2012.0 # 3 東京 2013.0
再インデックス reindex
新しいインデックスで再構成
reindexのメソッド
index | 行に対する新しいインデックスを指定 |
columns | 列に対する新しいインデックスを指定 |
method | ffill, pad:前方穴埋め、 bfill, backfill:後方穴埋め |
fill_value | 欠損を埋める値 |
limit | method梅のときの最大個数 |
level | 階層型インデックスの階層指定 |
copy | 変更前と同じインデックスのとき、Trueならコピー。デフォルトはTrue |
テストデータ(7) 東京の年平均気温
東京の平均気温(気象庁のサイトより)
# coding: utf-8 import pandas as pd url = 'http://python-remrin.hatenadiary.jp/entry/2017/05/18/142423' ## DataFrameのリストを得る。header=0のオプション指定で、最初の行をheader扱い。 fetched = pd.io.html.read_html(url) print(len(fetched)) # listの要素であるDataFrameの個数 df = fetched[0] # 2次元リスト化 data = [] for i in range(len(df)): x = [] for j in range(len(df.iloc[0])): x.append(df.iloc[i, j]) data.append(x) print(data) # hatena表組み化 for i in range(len(df)): s = "|" for j in range(len(df.iloc[0])): s += str(df.iloc[i, j]) + "|" print(s)
続きを読む
最小二乗法
最小二乗法について。
(2, 3), (4, 7), (9, 11)の3点をデータとして、最小二乗法を1次式で行う場合。
とすると、
これの最小値を求めることになり、偏微分をして
となるベクトルが係数ベクトルとなる。
# coding: utf-8 import numpy as np import matplotlib.pyplot as plt data = [(2, 3), (4, 7), (9, 11)] a = np.matrix(data) b = a[:, 1].copy() a[:, 1] = 1 # 係数ベクトルを求める x = ((a.T * a)**-1) * a.T * b x= np.array(x) # matplotlibで描画 datax, datay = np.split(np.array(data), 2, axis=1) maxx, maxy = max(datax), max(datay) plt.xlim(0, maxx*1.1) plt.ylim(0, maxy*1.1) plt.scatter(datax, datay) plt.plot([0, maxx*1.1], [x[1], maxx*x[0]*1.1 + x[1]], color="r") plt.text(1, maxy, "y={:.2f}x+{:.2f}".format(*x[:, 0]), fontsize=14) plt.show()
2次式以上でも最小二乗法で近似できるようにプログラムを作ってみました。
# coding: utf-8 import numpy as np import matplotlib.pyplot as plt import pandas as pd # 東京の過去140年間の平均気温のデータを取得 url = 'http://python-remrin.hatenadiary.jp/entry/2017/05/18/142423' ## DataFrameのリストを得る。header=0のオプション指定で、最初の行をheader扱い。 fetched = pd.io.html.read_html(url, header=0) df = fetched[0] # 2次元リスト化 data = [] for i in range(len(df)): x = [] for j in range(len(df.iloc[0])): x.append(df.iloc[i, j]) data.append(x) data = np.array(data) xlabel = data[0:, 0] datasize = data.shape[0] datax = np.arange(datasize) datay = np.array(data[:, -1], dtype=float) # n次式で近似するための行列を作る n = 2 a = np.empty((n + 1, datasize), dtype=int) for i in range(n + 1): a[i] = datax**(n - i) a = np.matrix(a.T) b = np.matrix(datay).T ## 係数ベクトルを求める x = ((a.T * a)**-1) * a.T * b x= np.array(x) def f(a): fa = 0 for i in range(n + 1): fa += x[i] * a**(n-i) return fa def textf(): txt = "y=" for i in range(n): txt +="{:+.3f}x^{}".format(x[i][0], n-i) txt += "{:+.1f}".format(x[n][0]) return txt print(x) print(textf()) # matplotlibで描画 maxx, maxy = max(datax), max(datay) plt.xlim(0, maxx*1) plt.ylim(10, maxy*1.1) plt.scatter(datax, datay) plt.plot(datax, f(datax), color="r") # 近似曲線、直線 plt.text(1, maxy, textf(), fontsize=14) # 近似式 plt.xticks(np.arange(datasize)[::20], xlabel[::20]) # 横軸目盛りは20ごと plt.title("mean temperature in Tokyo") plt.show()
東京の過去140年の平均気温を最小二乗法で近似。
1次式で最小二乗法
2次式で最小二乗法
ただし、3次以上だとうまくいかないので、原因調査中
参考:
正規方程式の導出と計算例 | 高校数学の美しい物語
NumPyの使い方(18) 次元操作
NumPyの次元操作について
次元の増減 | 保持 | 削減 | 増加 | |
---|---|---|---|---|
reshape | ○ | ○ | ○ | |
resize | ○ | ○ | ○ | |
flatten | ○ | × | ||
rabel | ○ | × | ||
any, all | × | ○ | × | |
sort | ○ | △ | × | axis略で1d |
スライス | ○ | × | × | |
インデックス | × | ○ | × | |
ファンシーインデックス | ○ | ○ | × | |
take | ○ | △ | × | axis略で1d |
mean, sum | × | ○ | × | |
min, max | × | ○ | × | |
concatenate | ○ | × | × | |
split | ○ | × | × | |
repeat | ○ | △ | × | axis略で1d |
tile | ○ | × | ○ | |
expand_dims | × | × | ○ | |
ブロードキャスト | ○ | × | ○ | |
イテレート | × | ○ | × | 次元が1減る |
compress | ○ | △ | × | axis略で1d |
・np.expand_dims()
軸を指定して次元を増やす。
import numpy as np a1 = np.array([0, 1, 2]) a2 = np.expand_dims(a1, axis=0) print(a2) # [[0 1 2]] # 以下も同義 a3 = a1[np.newaxis, :] print(a3) # [[0 1 2]] a4 =np.expand_dims(a1, axis=1) print(a4) # [[0] # [1] # [2]] # 以下も同義 a5 = a1[:, np.newaxis] print(a5) #[[0] # [1] # [2]]
NumPyの使い方(17) 構造化配列
NumPyの構造化配列(structured array)について。
dtypeを自分で設定して、異なる型の変数を織り込める。
フィールド名で要素を参照できる。
dtype = [("x", int), ("y", float)] a1 = np.array([(1, 2), (3, 4), (5, 6)], dtype=dtype) print(a1) # [(1, 2.0) (3, 4.0) (5, 6.0)] print(a1["x"]) # [1 3 5 ] print(a1["y"]) # [ 2. 4. 6.]
フィールドのsizeを指定したりもできる。
dtype = [("x", int, 3), ("y", float)] a2= np.array(([1, 2, 3], 4), dtype=dtype) print(a2["y"]) # 4.0 a3 = np.zeros(5, dtype=dtype) print(a3["x"]) # [[0 0 0] # [0 0 0] # [0 0 0] # [0 0 0] # [0 0 0]] print(a3["y"]) # [ 0. 0. 0. 0. 0.]
フィールドのネストもできる。
print(a3["y"]) # [ 0. 0. 0. 0. 0.] dtype = [("x", [("y", float), ("z", int)]), ("a", int)] a4 = np.array([((1, 2), 3), ((4, 5), 6)], dtype=dtype) print(a4["x"]) # [(1.0, 2) (4.0, 5)] print(a4["x"]["z"]) # [2 5]
麻雀(1)
何年かかるかわかりませんがプログラムの練習で麻雀を作ってみようと思います。
m | 萬子 | 0-8 |
p | 筒子 | 9-17 |
s | 索子 | 18-26 |
h | 字牌 | 東:27~中:33 |
pair | 対子 |
triplet | 暗刻 |
Triplet | 明刻 |
seq | 順子 |
kan | 暗槓 |
Kan | 明槓 |
prevailing_wind | 場風 |
seat_wind | 自風 |
dealer | 親 |
wall_seq | 136枚の牌をシャッフルしたシーケンス。 |
hand | 手牌。1次元ndarrayでsize34 |
文字列入力をhand配列へ。
import numpy as np ji_dic2= dict((s, i + 27) for i, s in enumerate("東南西北白發中")) def str_to_hand(string): hand = np.zeros(34, dtype=int) suit = np.zeros(9, dtype=int) while string: if string[0] in "123456789": suit[int(string[0]) - 1] +=1 elif string[0] in "mps": mps = (string[0]=="p")*9 + (string[0]=="s")*18 hand[mps:mps+9] += suit suit.fill(0) else: hand[ji_dic2[string[0]]] += 1 string = string[1:] return hand data = ["123m123p123789s中中"] data.append("1133557799m1133p") data.append("19m19p19s東南西北白發中") data.append("123m123p123789s白中") data.append("111222333999m11s") data.append("112233777999m11p") data.append("白1m1s白白1m1m222m11s中中") for i, hand in enumerate(data): print(i, str_to_hand(data[i])) # 0 [1 1 1 ..., 0 0 2] # 1 [2 0 2 ..., 0 0 0] # 2 [1 0 0 ..., 1 1 1] # 3 [1 1 1 ..., 1 0 1] # 4 [3 3 3 ..., 0 0 0] # 5 [2 2 2 ..., 0 0 0] # 6 [3 3 0 ..., 3 0 2]
クラスで作ってみると
import numpy as np ji_dic2= dict((s, i + 27) for i, s in enumerate("東南西北白發中")) class Hand(): def __init__(self, hand): if isinstance(hand, str): hand = self.str_to_hand(hand) self.hand = hand def str_to_hand(self, string): hand = np.zeros(34, dtype=int) suit = np.zeros(9, dtype=int) string = string.replace(" ", "").replace("[", "").replace("]", "") while string: if string[0] in "123456789": suit[int(string[0]) - 1] +=1 elif string[0] in "mps": mps = (string[0]=="p")*9 + (string[0]=="s")*18 hand[mps:mps+9] += suit suit.fill(0) elif string[0] in "東南西北白發中": hand[ji_dic2[string[0]]] += 1 string = string[1:] return hand def hand_to_str(self): pass if __name__ == "__main__": data = ["123m123p123789s中中"] data.append("1133557799m1133p") data.append("19m19p19s東南西北白發中中") data.append("123m123p123789s白中") data.append("111222333999m11s") data.append("112233777999m11p") data.append("白1m1s白白1m1m222m11s 中中") for i, hand in enumerate(data): h = Hand(hand).hand print(i, h) # 0 [1 1 1 ..., 0 0 2] # 1 [2 0 2 ..., 0 0 0] # 2 [1 0 0 ..., 1 1 2] # 3 [1 1 1 ..., 1 0 1] # 4 [3 3 3 ..., 0 0 0] # 5 [2 2 2 ..., 0 0 0] # 6 [3 3 0 ..., 3 0 2]
役判定から点数計算までの流れ(仮)
・手牌の形だけで判断できる役
・国士無双かどうか
・七対子かどうか
・その他:1雀頭、4面子になっているか
・まず雀頭で場合分け
・刻子で場合分け
・残りが順子か
・手牌の形と状態(面前かどうかなど)で判断できる役
・風牌
・状態で判断できる役(天和、地和、海底、嶺上、立直、一発、ツモなど)
・ドラのせ
・点数計算
# coding: utf-8 import numpy as np from itertools import combinations ji_dic = dict((i + 27, s) for i, s in enumerate("東南西北白發中")) ji_dic2= dict((s, i + 27) for i, s in enumerate("東南西北白發中")) yaku_dic = {0:"7 pairs", 1:"13 orphans", 2:"Concealed four pongs"} def hand_to_str(hand): string = str(np.repeat(np.arange(1, 10), hand[:9])) string += "m " * any(hand[:9]) string += str(np.repeat(np.arange(1, 10), hand[9:18])) string += "p " * any(hand[9:18]) string += str(np.repeat(np.arange(1, 10), hand[18:27])) string += "s " * any(hand[18:27]) for i in range(27, 34): string += ji_dic[i] * hand[i] return string def is_winhand(hand): # seven pairs seven_pairs = sum(hand == 2) == 7 yaku = "seven_pairs" if seven_pairs else "" # 13 orphans if np.all(hand[[0, 8, 9, 17, 18, 26, 27, 28, 29, 30, 31, 32, 33]] > 0): return "13 orphans" else: result = melding(hand) return yaku if yaku else result if result else "not a winning hand" # parsing def melding(tiles): # 順子さがし def find_seq(tiles, melds): while sum(tiles): where = np.where(tiles > 0)[0] # 牌が存在する位置の0次元目→横方向インデックス if len(where)>2 and where[0] + 2 == where[1] + 1 == where[2]: melds.append((where[0], "seq")) tiles[where[0]] -= 1 tiles[where[1]] -= 1 tiles[where[2]] -= 1 else: melds = [] break return melds # 刻子さがし def find_triplet(tiles, pair): items = np.where(tiles > 2)[0] triplets = [] # find all subsets of triplets(刻子の全組み合わせを列挙) for i in range(len(items) + 1): for c in combinations(items, i): triplets.append(c) # find seq for each triplet-subset(それぞれについて順子さがし) result=[] for triplet in triplets: melds = [pair] tile = tiles.copy() for t in triplet: melds.append((t, "triplet")) tile[t] -= 3 seq = find_seq(tile, melds) if seq: result.append(seq) return result # find pair(対子さがし) m = [] for i in np.where(tiles > 1)[0]: hand = tiles.copy() meld = (i, "pair") hand[i] -= 2 meld = find_triplet(hand, meld) if meld: m.append(meld) return m if m else "" class Hand(): def __init__(self, hand): #手牌が配列ではなく文字列なら、配列に変換 if isinstance(hand, str): hand = self.str_to_hand(hand) self.hand = hand # 文字入力をNumPy配列に変換 def str_to_hand(self, string): hand = np.zeros(34, dtype=int) suit = np.zeros(9, dtype=int) string = string.replace(" ", "").replace("[", "").replace("]", "") while string: if string[0] in "123456789": suit[int(string[0]) - 1] +=1 elif string[0] in "mps": mps = (string[0]=="p")*9 + (string[0]=="s")*18 hand[mps:mps+9] += suit suit.fill(0) elif string[0] in "東南西北白發中": hand[ji_dic2[string[0]]] += 1 string = string[1:] return hand if __name__ == "__main__": data = ["123m123p123789s中中"] data.append("1133557799m1133p") # 7 pairs data.append("19m19p19s東南西北白發中中") # 13 orphans data.append("123m123p123789s白中") # not a winning hand data.append("111222333999m11s") data.append("112233777999m11p") data.append("白1m1s白白1m1m222m11s 中中") for i, hand in enumerate(data): h = Hand(hand).hand print(i, is_winhand(h)) # 0 [[[(33, 'pair'), (0, 'seq'), (9, 'seq'), (18, 'seq'), (24, 'seq')]]] # 1 seven_pairs # 2 13 orphans # 3 not a winning hand # 4 [[[(18, 'pair'), (8, 'triplet'), (0, 'seq'), (0, 'seq'), (0, 'seq')], [(18, 'pair'), (0, 'triplet'), (1, 'triplet'), (2, 'triplet'), (8, 'triplet')]]] # 5 [[[(9, 'pair'), (6, 'triplet'), (8, 'triplet'), (0, 'seq'), (0, 'seq')]]] # 6 [[[(33, 'pair'), (0, 'triplet'), (1, 'triplet'), (18, 'triplet'), (31, 'triplet')]]]
NumPyの使い方(16) 行列matrix
NumPyのmatrixについて。
class | 種類 | 次元 |
---|---|---|
ndarray | 配列 | 次元は自由 |
matrix | 行列 | 2次元に固定 |
ndarrayでは行列の積を表すときにdot()を呼びだすが、
matrixでは「*」や「**」で行列の積などを表せる。
その他の使い方はndarrayと同様のようです。
import numpy as np a1 = np.array([[1, 2], [3, 4]]) a2 = np.array([[1, 1], [1, 1]]) print(a1 * a2) #同じ位置の要素どうしの積 # [[1 2] # [3 4]] print(a1.dot(a2)) # 内積 # [[3 3] # [7 7]] m1 = np.matrix([[1, 2], [3, 4]]) m2 = np.matrix([[1, 1], [1, 1]]) print(m1 * m2) # [[3 3] # [7 7]] print(m1 ** -1) # 逆行列(1) # [[-2. 1. ] # [ 1.5 -0.5]] print(m1.I) # 逆行列(2) # [[-2. 1. ] # [ 1.5 -0.5]] m3 = np.matrix([[1, 3], [-2, -4]]) P = (np.linalg.eig(m3))[1] # 正規化された固有ベクトルから作った対角化行列 print(P) # [[ 0.83205029 -0.70710678] # [-0.5547002 0.70710678]] PiAP = P.I * m3 * P # 対角化された行列 PiAP = PiAP.round(3) # 見やすくするため四捨五入 print(PiAP) # [[-1. 0.] # [-0. -2.]] print((PiAP)**2) # [[ 1. 0.] # [ 0. 4.]] print((PiAP)**3) # [[-1. 0.] # [-0. -8.]] # ndarrayでの内積の書き方 a3 = np.array(PiAP) print(PiAP.dot(PiAP).dot(PiAP).dot(PiAP)) # [[ 1. 0.] # [ 0. 16.]]
行列の積などを多用するプログラムのときはmatrixを用いると見やすく書けそう。
操作・演算 | ndarray | matrix |
---|---|---|
* | 対応要素の積 | 内積matrix product |
** | 各要素の2乗 | 行列の2乗 |
*-1 | 各要素の逆数 | 逆行列inverse |
I属性 | なし | 逆行列inverse |
H属性 | なし | 共役転置conjugate transpose |
matrixは2次元固定なので次元削減の操作にも一部違いがあります。
axisを指定したmean()だと、2次元(ndim=2)のままです。
m1 = np.matrix([5, 1]) print(m1) # [[5 1]] print(m1.shape) # (1, 2) print(m1.mean(1)) # [[3]] # 2次元のまま print(m1.mean(0)) # [[ 5. 1.]] print(m1.mean()) # 3.0
特にこだわりがなければ、matrixを使わずに、ndarrayだけで必要な操作ができるみたいです。