PSM

python する man

Pythonで麻雀の戦績を集計してみた

いつか集計しようと思って,趣味の麻雀の記録をexcelでとっていたのですが,この度ついに実行に移すことになりました.

データ読み込み

exelで集計していたデータをpythonで読み込みます.
使用環境は3系(python3.6.0)でJupyter Notebookを利用しています.

今回はpandasのread_csv()を利用して読み込みました.
エクセルで作成したファイルを[cmd+shift+s]でファイル形式をcsvに変えて別名保存しておきます.

pd.read_csv("../Data/mahjong_log.csv")

としてみると,

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8c in position 1: invalid start byte

と怒られます.
read_table()を試してみても,やはり同じエラー.
文字コードをencoding="sjis"で指定する必要がありました.

import pandas as pd
pd.read_csv("hoge.csv", encoding="sjis")

データ加工

こんな感じのデータを読み込みます.
f:id:muromura:20170510223606p:plain
列が各対局者,行が半荘ごとの成績(得点)となっています.
対局に参加していない人の欄は空白になっており,pythonではNaNとして読み込まれます.

3行目から6行目はエクセルで順位つけようとして途中で面倒になってやめた名残です.
不要行削除の例示に都合良いのでそのままにしておきます.

1行を削除するときには,配列のスライスと同じように指定すれば良いです.

#不要部分削除
hoge[:5]

列を削除するときにはdrop()を使います.

#不要部分削除
data = data.drop("削除したい列名", axis=1)

データ集計

対局数をカウントするのには,NaNの数を利用することにしました.
各列ごとにis.null()でNaNの数を調べるとTrue/Falseで結果が帰ってきます.
pythonにおいてはTrue/Falseは数値計算すると1/0に等しくなるので,結果を1から引いてやることで各列で対局に参加した部分(=得点が入力されている部分)を1,それ以外を0とすることができました.
あとはそれを合計するだけです.

#対局数
(1-data.isnull()).sum()


順位の集計にはちょっと手間取りました.
rank()を使えば良いのですが,このとき行ごとに順位を出すには,引数でaxis=1を指定します.
ちなみにこのときNaNはNaNのまま無視してくれました.便利!

rank()を使うと数が少ない順に1から数字が振られるので,本来の順位とは逆になってしまいます.
それをひっくり返すためには5から順位を引けば良いことになります.
これはpythonのブロードキャストを使って簡単に書けました.

#順位づけ
rank = data.rank(axis=1)
rank= 5 - rank

次に,1位から4位までそれぞれの順位を何回とったか数えます.
たぶん他にもっと良い方法があると思いますが,思いつかなかったので,各列ごとに抜き出してvalue_counts()で集計し,後でそれを全部くっつけるという方法をとることにしました,
最初に空のDataFrameを用意し,colmuns()でとってきた列名でfor文を回して集計結果をくっつけています.
DataFrameの結合にはconcat()を使い,列方向にくっつけたいときにはaxis=1を指定します.

#各順位を何回とったかの集計
#列名を取得
players = rank.columns
#空のデータフレームを用意
ranks = pd.DataFrame(index=[1,2,3,4], columns=[])
#for文で回してranksに突っ込んでいく.
for player in players:
    r = rank[player].value_counts()
    ranks = pd.concat([ranks, r], axis=1)

サンプルコード

というわけで長くなってしまいましたが,サンプルコードは以下.

import numpy as np
import pandas as pd

#データ読み込み
raw_data = pd.read_csv("sample.csv", encoding="sjis")

#不要な部分を切り捨てる
data = pd.DataFrame(raw_data[5:])
data = data.drop("day", axis=1)
#型をチェック
#data.dtypes

#順位づけ
rank = data.rank(axis=1)
rank= 5 - rank

#対局数
(1-data.isnull()).sum()

#平均順位算
rank.mean()

#合計計算
data.sum()

#平均得点計算
data.mean()

#順位合計
players = rank.columns
players
ranks = pd.DataFrame(index=[1,2,3,4], columns=[])
for player in players:
    r = rank[player].value_counts()
    ranks = pd.concat([ranks, r], axis=1)

#集計
result = pd.concat([(1-data.isnull()).sum(),
                    ranks.T,
                    rank.mean(),
                    data.sum(), 
                    data.mean()],
                   axis=1, join='inner').T

result.index=['対局数',"1位","2位","3位","4位",'平均順位','合計得点','平均得点']
result = result.round(2)

結果はこんな感じになります.
f:id:muromura:20170510224213p:plain


また時間のあるときに,次は各日ごとの集計をしたり,player同士の相性を計算したりなどやっていきたいと思います.
適当ですが,得点の相関係数とかでも相性の指標になりうるかもしれませんね.