libs.data.aggregate

libs/data/aggregate.py

  1"""
  2libs/data/aggregate.py
  3"""
  4
  5import logging
  6from typing import Optional
  7
  8import numpy as np
  9import pandas as pd
 10
 11import libs.global_value as g
 12from libs.data import loader
 13from libs.utils import formatter
 14
 15
 16def game_summary(
 17    filter_items: Optional[list] = None,
 18    drop_items: Optional[list] = None,
 19) -> pd.DataFrame:
 20    """ゲーム結果をサマライズする
 21
 22    Args:
 23        filter_items (Optional[list], optional): 抽出するカラム. Defaults to None.
 24        drop_items (Optional[list], optional): 除外するカラム. Defaults to None.
 25
 26    Returns:
 27        pd.DataFrame: 集計結果
 28    """
 29
 30    # データ収集
 31    df = loader.read_data("SUMMARY_TOTAL")
 32
 33    if isinstance(filter_items, list):
 34        df = df.filter(items=filter_items)
 35
 36    if isinstance(drop_items, list):
 37        df = df.drop(columns=drop_items)
 38
 39    logging.trace(df)  # type: ignore
 40    return df
 41
 42
 43def game_results() -> pd.DataFrame:
 44    """成績を集計する
 45
 46    Returns:
 47        pd.DataFrame: 集計結果
 48    """
 49
 50    # データ収集
 51    df = loader.read_data("SUMMARY_RESULTS")
 52
 53    # Nullが返ってきたときにobject型になるので型変換
 54    df = df.astype({
 55        "東家-平均順位": "float", "南家-平均順位": "float", "西家-平均順位": "float", "北家-平均順位": "float",
 56        "東家-役満和了": "Int64", "南家-役満和了": "Int64", "西家-役満和了": "Int64", "北家-役満和了": "Int64",
 57    }).fillna(0)
 58
 59    # インデックスの振り直し
 60    df = df.reset_index(drop=True)
 61    df.index = df.index + 1
 62
 63    logging.trace(df)  # type: ignore
 64    return df
 65
 66
 67# ランキング
 68def ranking_record() -> pd.DataFrame:
 69    """ランキング集計
 70
 71    Returns:
 72        pd.DataFrame: 集計結果
 73    """
 74
 75    # データ収集
 76    gamedata = loader.read_data("RANKING_RECORD_COUNT")
 77    player_list = gamedata["name"].unique().tolist()
 78
 79    # 連続順位カウント
 80    rank_mask = {
 81        "c_top": {1: 1, 2: 0, 3: 0, 4: 0},  # 連続トップ
 82        "c_top2": {1: 1, 2: 1, 3: 0, 4: 0},  # 連続連対
 83        "c_top3": {1: 1, 2: 1, 3: 1, 4: 0},  # 連続ラス回避
 84        "c_low": {1: 0, 2: 1, 3: 1, 4: 1},  # 連続トップなし
 85        "c_low2": {1: 0, 2: 0, 3: 1, 4: 1},  # 連続逆連対
 86        "c_low4": {1: 0, 2: 0, 3: 0, 4: 1},  # 連続ラス
 87    }
 88
 89    record_df = pd.DataFrame(
 90        {
 91            "name": player_list,
 92            "c_top": [0 for _ in player_list],
 93            "c_top2": [0 for _ in player_list],
 94            "c_top3": [0 for _ in player_list],
 95            "c_low": [0 for _ in player_list],
 96            "c_low2": [0 for _ in player_list],
 97            "c_low4": [0 for _ in player_list],
 98        },
 99        index=player_list
100    )
101
102    for key, val in rank_mask.items():
103        for pname in player_list:
104            tmp_df = pd.DataFrame()
105            tmp_df["flg"] = gamedata.query(
106                "name == @pname"
107            )["順位"].replace(val)
108
109            tmp_df[key] = tmp_df["flg"].groupby(
110                (tmp_df["flg"] != tmp_df["flg"].shift()).cumsum()
111            ).cumcount() + 1
112            tmp_df.loc[tmp_df["flg"] == 0, key] = 0
113            max_key = key.replace("c_", "max_")
114            record_df.at[pname, max_key] = int(tmp_df[[key]].max().values[0])
115
116            # 最終値
117            record_df.at[pname, key] = tmp_df[key].iloc[-1]
118            record_df[max_key] = record_df[max_key].fillna(0).copy().astype("int")
119
120    # 最大値/最小値追加
121    if not gamedata.empty:
122        record_df["point_max"] = gamedata["point_max"].iloc[0]
123        record_df["point_min"] = gamedata["point_min"].iloc[0]
124        record_df["rpoint_max"] = gamedata["rpoint_max"].iloc[0]
125        record_df["rpoint_min"] = gamedata["rpoint_min"].iloc[0]
126
127    logging.trace(record_df)  # type: ignore
128    return record_df
129
130
131def calculation_rating() -> pd.DataFrame:
132    """レーティング集計
133
134    Returns:
135        pd.DataFrame: 集計結果
136    """
137
138    # データ収集
139    df_results = loader.read_data("RANKING_RATINGS").set_index("playtime")
140    df_ratings = pd.DataFrame(index=["initial_rating"] + df_results.index.to_list())  # 記録用
141    last_ratings: dict = {}  # 最終値格納用
142
143    # 獲得スコア
144    score_mapping = {"1": 30.0, "2": 10.0, "3": -10.0, "4": -30.0}
145
146    for x in df_results.itertuples():
147        player_list = (x.p1_name, x.p2_name, x.p3_name, x.p4_name)
148        for player in player_list:
149            if player not in df_ratings.columns:
150                last_ratings[player] = 1500.0
151                df_ratings[player] = np.nan
152                df_ratings.loc["initial_rating", player] = 1500.0
153                df_ratings = df_ratings.copy()
154
155        # 天鳳計算式 (https://tenhou.net/man/#RATING)
156        rank_list = (x.p1_rank, x.p2_rank, x.p3_rank, x.p4_rank,)
157        rating_list = [last_ratings[player] for player in player_list]
158        rating_avg = 1500.0 if np.mean(rating_list) < 1500.0 else np.mean(rating_list)
159
160        for i, player in enumerate(player_list):
161            rating = float(rating_list[i])
162            rank = str(rank_list[i])
163
164            correction_value: float = (rating_avg - rating) / 40
165            if df_ratings[player].count() >= 400:
166                match_correction = 0.2
167            else:
168                match_correction = 1 - df_ratings[player].count() * 0.002
169
170            new_rating = rating + match_correction * (score_mapping[rank] + correction_value)
171
172            last_ratings[player] = new_rating
173            df_ratings.loc[x.Index, player] = new_rating
174
175    # 間引き(集約オプション)
176    if (collection := g.params.get("collection")):
177        ratings = df_ratings[1:]
178        ratings.index = pd.to_datetime(ratings.index)  # DatetimeIndexに変換
179
180        match collection:
181            case "daily":
182                ratings = ratings.resample("D").last().ffill()
183            case "monthly":
184                ratings = ratings.resample("ME").last().ffill()
185            case "yearly":
186                ratings = ratings.resample("YE").last().ffill()
187            case "all":
188                ratings = df_ratings.ffill().tail(1)
189            case _:
190                return df_ratings
191
192        ratings.index = ratings.index.astype(str)
193        df_ratings = pd.concat([df_ratings.head(1), ratings])
194
195    return df_ratings
196
197
198def grade_promotion_check(
199    grade_level: int,
200    point: int,
201    rank: int
202) -> tuple[int, int]:
203    """昇段チェック
204
205    Args:
206        grade_level (int): 現在のレベル(段位)
207        point (int): 現在の昇段ポイント
208        rank (int): 獲得順位
209
210    Returns:
211        tuple[int, int]: チェック後の昇段ポイント, チェック後のレベル(段位)
212    """
213
214    tbl_data = g.cfg.badge.grade.table["table"]
215    new_point = point + int(tbl_data[grade_level]["acquisition"][rank - 1])
216
217    if new_point >= int(tbl_data[grade_level]["point"][1]):  # level up
218        grade_level = min(grade_level + 1, len(tbl_data) - 1)
219        new_point = int(tbl_data[grade_level]["point"][0])  # 初期値
220    elif new_point < 0:  # level down
221        new_point = int(0)
222        if tbl_data[grade_level]["demote"]:
223            grade_level = max(grade_level - 1, 0)
224            new_point = int(tbl_data[grade_level]["point"][0])  # 初期値
225
226    return (new_point, grade_level)
227
228
229# レポート
230def matrix_table() -> pd.DataFrame:
231    """対局対戦マトリックス表の作成
232
233    Returns:
234        pd.DataFrame: 集計結果
235    """
236
237    # データ収集
238    df = loader.read_data("REPORT_MATRIX_TABLE").set_index("playtime")
239
240    # 結果に含まれるプレイヤーのリスト
241    plist = sorted(list(set(
242        df["p1_name"].tolist() + df["p2_name"].tolist() + df["p3_name"].tolist() + df["p4_name"].tolist()
243    )))
244
245    # 順位テーブルの作成
246    l_data: dict = {}
247    for pname in plist:
248        if g.params.get("individual"):  # 個人集計
249            l_name = formatter.name_replace(pname)
250            # プレイヤー指定があるなら対象以外をスキップ
251            if g.params["player_list"]:
252                if l_name not in g.params["player_list"].values():
253                    continue
254            # ゲスト置換
255            if g.params.get("guest_skip"):  # ゲストあり
256                l_name = formatter.name_replace(pname, add_mark=True)
257            else:  # ゲストなし
258                if pname == g.cfg.member.guest_name:
259                    continue
260        else:  # チーム集計
261            l_name = pname
262
263        l_data[l_name] = []
264        for x in df.itertuples():
265            match pname:
266                case x.p1_name:
267                    l_data[l_name] += [x.p1_rank]
268                case x.p2_name:
269                    l_data[l_name] += [x.p2_rank]
270                case x.p3_name:
271                    l_data[l_name] += [x.p3_rank]
272                case x.p4_name:
273                    l_data[l_name] += [x.p4_rank]
274                case _:
275                    l_data[l_name] += [None]
276
277    # 規定打数以下を足切り
278    if g.params["stipulated"]:
279        for pname in list(l_data.keys()):
280            if sum(x is not None for x in l_data[pname]) < g.params["stipulated"]:
281                l_data.pop(pname)
282
283    rank_df = pd.DataFrame(
284        l_data.values(),
285        columns=list(df.index),
286        index=list(l_data.keys())
287    )
288
289    # 対象リストが0件になった場合は空のデータフレームを返す
290    if rank_df.empty:
291        return rank_df
292
293    # 対局対戦マトリックス表の作成
294    mtx_df = pd.DataFrame(
295        index=list(l_data.keys()),
296        columns=list(l_data.keys()) + ["total"]
297    )
298    sorting_df = pd.DataFrame(
299        index=list(l_data.keys()),
300        columns=["win_per", "count"]
301    )
302
303    for idx1 in range(len(rank_df)):
304        p1 = rank_df.iloc[idx1]
305        t_game_count = 0
306        t_win = 0
307        for idx2 in range(len(rank_df)):
308            p2 = rank_df.iloc[idx2]
309            if p1.name == p2.name:
310                mtx_df.loc[f"{p1.name}", f"{p2.name}"] = "---"
311            else:
312                game_count = len(pd.concat([p1, p2], axis=1).dropna())
313                win = (p1 < p2).sum()
314                t_game_count += game_count
315                t_win += win
316
317                if game_count:
318                    winning_per = str(round(float(win / game_count * 100), 1))
319                else:
320                    winning_per = "--.-"
321                mtx_df.loc[f"{p1.name}", f"{p2.name}"] = f"{win}-{game_count - win} ({winning_per}%)"
322
323        if t_game_count:
324            t_winning_per = str(round(float(t_win / t_game_count * 100), 1))
325        else:
326            t_winning_per = "--.-"
327        mtx_df.loc[f"{p1.name}", "total"] = f"{t_win}-{t_game_count - t_win} ({t_winning_per}%)"
328        sorting_df.loc[f"{p1.name}", "win_per"] = t_winning_per
329        sorting_df.loc[f"{p1.name}", "count"] = t_game_count
330
331    # 勝率で並び替え
332    sorting_df["win_per"] = pd.to_numeric(sorting_df["win_per"], errors="coerce")
333    sorting_df["count"] = pd.to_numeric(sorting_df["count"], errors="coerce")
334    sorting_df = sorting_df.sort_values(by=["win_per", "count"], ascending=[False, False])
335    mtx_df = mtx_df.reindex(
336        index=list(sorting_df.index),
337        columns=list(sorting_df.index) + ["total"]
338    )
339
340    return mtx_df
def game_summary( filter_items: Optional[list] = None, drop_items: Optional[list] = None) -> pandas.core.frame.DataFrame:
17def game_summary(
18    filter_items: Optional[list] = None,
19    drop_items: Optional[list] = None,
20) -> pd.DataFrame:
21    """ゲーム結果をサマライズする
22
23    Args:
24        filter_items (Optional[list], optional): 抽出するカラム. Defaults to None.
25        drop_items (Optional[list], optional): 除外するカラム. Defaults to None.
26
27    Returns:
28        pd.DataFrame: 集計結果
29    """
30
31    # データ収集
32    df = loader.read_data("SUMMARY_TOTAL")
33
34    if isinstance(filter_items, list):
35        df = df.filter(items=filter_items)
36
37    if isinstance(drop_items, list):
38        df = df.drop(columns=drop_items)
39
40    logging.trace(df)  # type: ignore
41    return df

ゲーム結果をサマライズする

Arguments:
  • filter_items (Optional[list], optional): 抽出するカラム. Defaults to None.
  • drop_items (Optional[list], optional): 除外するカラム. Defaults to None.
Returns:

pd.DataFrame: 集計結果

def game_results() -> pandas.core.frame.DataFrame:
44def game_results() -> pd.DataFrame:
45    """成績を集計する
46
47    Returns:
48        pd.DataFrame: 集計結果
49    """
50
51    # データ収集
52    df = loader.read_data("SUMMARY_RESULTS")
53
54    # Nullが返ってきたときにobject型になるので型変換
55    df = df.astype({
56        "東家-平均順位": "float", "南家-平均順位": "float", "西家-平均順位": "float", "北家-平均順位": "float",
57        "東家-役満和了": "Int64", "南家-役満和了": "Int64", "西家-役満和了": "Int64", "北家-役満和了": "Int64",
58    }).fillna(0)
59
60    # インデックスの振り直し
61    df = df.reset_index(drop=True)
62    df.index = df.index + 1
63
64    logging.trace(df)  # type: ignore
65    return df

成績を集計する

Returns:

pd.DataFrame: 集計結果

def ranking_record() -> pandas.core.frame.DataFrame:
 69def ranking_record() -> pd.DataFrame:
 70    """ランキング集計
 71
 72    Returns:
 73        pd.DataFrame: 集計結果
 74    """
 75
 76    # データ収集
 77    gamedata = loader.read_data("RANKING_RECORD_COUNT")
 78    player_list = gamedata["name"].unique().tolist()
 79
 80    # 連続順位カウント
 81    rank_mask = {
 82        "c_top": {1: 1, 2: 0, 3: 0, 4: 0},  # 連続トップ
 83        "c_top2": {1: 1, 2: 1, 3: 0, 4: 0},  # 連続連対
 84        "c_top3": {1: 1, 2: 1, 3: 1, 4: 0},  # 連続ラス回避
 85        "c_low": {1: 0, 2: 1, 3: 1, 4: 1},  # 連続トップなし
 86        "c_low2": {1: 0, 2: 0, 3: 1, 4: 1},  # 連続逆連対
 87        "c_low4": {1: 0, 2: 0, 3: 0, 4: 1},  # 連続ラス
 88    }
 89
 90    record_df = pd.DataFrame(
 91        {
 92            "name": player_list,
 93            "c_top": [0 for _ in player_list],
 94            "c_top2": [0 for _ in player_list],
 95            "c_top3": [0 for _ in player_list],
 96            "c_low": [0 for _ in player_list],
 97            "c_low2": [0 for _ in player_list],
 98            "c_low4": [0 for _ in player_list],
 99        },
100        index=player_list
101    )
102
103    for key, val in rank_mask.items():
104        for pname in player_list:
105            tmp_df = pd.DataFrame()
106            tmp_df["flg"] = gamedata.query(
107                "name == @pname"
108            )["順位"].replace(val)
109
110            tmp_df[key] = tmp_df["flg"].groupby(
111                (tmp_df["flg"] != tmp_df["flg"].shift()).cumsum()
112            ).cumcount() + 1
113            tmp_df.loc[tmp_df["flg"] == 0, key] = 0
114            max_key = key.replace("c_", "max_")
115            record_df.at[pname, max_key] = int(tmp_df[[key]].max().values[0])
116
117            # 最終値
118            record_df.at[pname, key] = tmp_df[key].iloc[-1]
119            record_df[max_key] = record_df[max_key].fillna(0).copy().astype("int")
120
121    # 最大値/最小値追加
122    if not gamedata.empty:
123        record_df["point_max"] = gamedata["point_max"].iloc[0]
124        record_df["point_min"] = gamedata["point_min"].iloc[0]
125        record_df["rpoint_max"] = gamedata["rpoint_max"].iloc[0]
126        record_df["rpoint_min"] = gamedata["rpoint_min"].iloc[0]
127
128    logging.trace(record_df)  # type: ignore
129    return record_df

ランキング集計

Returns:

pd.DataFrame: 集計結果

def calculation_rating() -> pandas.core.frame.DataFrame:
132def calculation_rating() -> pd.DataFrame:
133    """レーティング集計
134
135    Returns:
136        pd.DataFrame: 集計結果
137    """
138
139    # データ収集
140    df_results = loader.read_data("RANKING_RATINGS").set_index("playtime")
141    df_ratings = pd.DataFrame(index=["initial_rating"] + df_results.index.to_list())  # 記録用
142    last_ratings: dict = {}  # 最終値格納用
143
144    # 獲得スコア
145    score_mapping = {"1": 30.0, "2": 10.0, "3": -10.0, "4": -30.0}
146
147    for x in df_results.itertuples():
148        player_list = (x.p1_name, x.p2_name, x.p3_name, x.p4_name)
149        for player in player_list:
150            if player not in df_ratings.columns:
151                last_ratings[player] = 1500.0
152                df_ratings[player] = np.nan
153                df_ratings.loc["initial_rating", player] = 1500.0
154                df_ratings = df_ratings.copy()
155
156        # 天鳳計算式 (https://tenhou.net/man/#RATING)
157        rank_list = (x.p1_rank, x.p2_rank, x.p3_rank, x.p4_rank,)
158        rating_list = [last_ratings[player] for player in player_list]
159        rating_avg = 1500.0 if np.mean(rating_list) < 1500.0 else np.mean(rating_list)
160
161        for i, player in enumerate(player_list):
162            rating = float(rating_list[i])
163            rank = str(rank_list[i])
164
165            correction_value: float = (rating_avg - rating) / 40
166            if df_ratings[player].count() >= 400:
167                match_correction = 0.2
168            else:
169                match_correction = 1 - df_ratings[player].count() * 0.002
170
171            new_rating = rating + match_correction * (score_mapping[rank] + correction_value)
172
173            last_ratings[player] = new_rating
174            df_ratings.loc[x.Index, player] = new_rating
175
176    # 間引き(集約オプション)
177    if (collection := g.params.get("collection")):
178        ratings = df_ratings[1:]
179        ratings.index = pd.to_datetime(ratings.index)  # DatetimeIndexに変換
180
181        match collection:
182            case "daily":
183                ratings = ratings.resample("D").last().ffill()
184            case "monthly":
185                ratings = ratings.resample("ME").last().ffill()
186            case "yearly":
187                ratings = ratings.resample("YE").last().ffill()
188            case "all":
189                ratings = df_ratings.ffill().tail(1)
190            case _:
191                return df_ratings
192
193        ratings.index = ratings.index.astype(str)
194        df_ratings = pd.concat([df_ratings.head(1), ratings])
195
196    return df_ratings

レーティング集計

Returns:

pd.DataFrame: 集計結果

def grade_promotion_check(grade_level: int, point: int, rank: int) -> tuple[int, int]:
199def grade_promotion_check(
200    grade_level: int,
201    point: int,
202    rank: int
203) -> tuple[int, int]:
204    """昇段チェック
205
206    Args:
207        grade_level (int): 現在のレベル(段位)
208        point (int): 現在の昇段ポイント
209        rank (int): 獲得順位
210
211    Returns:
212        tuple[int, int]: チェック後の昇段ポイント, チェック後のレベル(段位)
213    """
214
215    tbl_data = g.cfg.badge.grade.table["table"]
216    new_point = point + int(tbl_data[grade_level]["acquisition"][rank - 1])
217
218    if new_point >= int(tbl_data[grade_level]["point"][1]):  # level up
219        grade_level = min(grade_level + 1, len(tbl_data) - 1)
220        new_point = int(tbl_data[grade_level]["point"][0])  # 初期値
221    elif new_point < 0:  # level down
222        new_point = int(0)
223        if tbl_data[grade_level]["demote"]:
224            grade_level = max(grade_level - 1, 0)
225            new_point = int(tbl_data[grade_level]["point"][0])  # 初期値
226
227    return (new_point, grade_level)

昇段チェック

Arguments:
  • grade_level (int): 現在のレベル(段位)
  • point (int): 現在の昇段ポイント
  • rank (int): 獲得順位
Returns:

tuple[int, int]: チェック後の昇段ポイント, チェック後のレベル(段位)

def matrix_table() -> pandas.core.frame.DataFrame:
231def matrix_table() -> pd.DataFrame:
232    """対局対戦マトリックス表の作成
233
234    Returns:
235        pd.DataFrame: 集計結果
236    """
237
238    # データ収集
239    df = loader.read_data("REPORT_MATRIX_TABLE").set_index("playtime")
240
241    # 結果に含まれるプレイヤーのリスト
242    plist = sorted(list(set(
243        df["p1_name"].tolist() + df["p2_name"].tolist() + df["p3_name"].tolist() + df["p4_name"].tolist()
244    )))
245
246    # 順位テーブルの作成
247    l_data: dict = {}
248    for pname in plist:
249        if g.params.get("individual"):  # 個人集計
250            l_name = formatter.name_replace(pname)
251            # プレイヤー指定があるなら対象以外をスキップ
252            if g.params["player_list"]:
253                if l_name not in g.params["player_list"].values():
254                    continue
255            # ゲスト置換
256            if g.params.get("guest_skip"):  # ゲストあり
257                l_name = formatter.name_replace(pname, add_mark=True)
258            else:  # ゲストなし
259                if pname == g.cfg.member.guest_name:
260                    continue
261        else:  # チーム集計
262            l_name = pname
263
264        l_data[l_name] = []
265        for x in df.itertuples():
266            match pname:
267                case x.p1_name:
268                    l_data[l_name] += [x.p1_rank]
269                case x.p2_name:
270                    l_data[l_name] += [x.p2_rank]
271                case x.p3_name:
272                    l_data[l_name] += [x.p3_rank]
273                case x.p4_name:
274                    l_data[l_name] += [x.p4_rank]
275                case _:
276                    l_data[l_name] += [None]
277
278    # 規定打数以下を足切り
279    if g.params["stipulated"]:
280        for pname in list(l_data.keys()):
281            if sum(x is not None for x in l_data[pname]) < g.params["stipulated"]:
282                l_data.pop(pname)
283
284    rank_df = pd.DataFrame(
285        l_data.values(),
286        columns=list(df.index),
287        index=list(l_data.keys())
288    )
289
290    # 対象リストが0件になった場合は空のデータフレームを返す
291    if rank_df.empty:
292        return rank_df
293
294    # 対局対戦マトリックス表の作成
295    mtx_df = pd.DataFrame(
296        index=list(l_data.keys()),
297        columns=list(l_data.keys()) + ["total"]
298    )
299    sorting_df = pd.DataFrame(
300        index=list(l_data.keys()),
301        columns=["win_per", "count"]
302    )
303
304    for idx1 in range(len(rank_df)):
305        p1 = rank_df.iloc[idx1]
306        t_game_count = 0
307        t_win = 0
308        for idx2 in range(len(rank_df)):
309            p2 = rank_df.iloc[idx2]
310            if p1.name == p2.name:
311                mtx_df.loc[f"{p1.name}", f"{p2.name}"] = "---"
312            else:
313                game_count = len(pd.concat([p1, p2], axis=1).dropna())
314                win = (p1 < p2).sum()
315                t_game_count += game_count
316                t_win += win
317
318                if game_count:
319                    winning_per = str(round(float(win / game_count * 100), 1))
320                else:
321                    winning_per = "--.-"
322                mtx_df.loc[f"{p1.name}", f"{p2.name}"] = f"{win}-{game_count - win} ({winning_per}%)"
323
324        if t_game_count:
325            t_winning_per = str(round(float(t_win / t_game_count * 100), 1))
326        else:
327            t_winning_per = "--.-"
328        mtx_df.loc[f"{p1.name}", "total"] = f"{t_win}-{t_game_count - t_win} ({t_winning_per}%)"
329        sorting_df.loc[f"{p1.name}", "win_per"] = t_winning_per
330        sorting_df.loc[f"{p1.name}", "count"] = t_game_count
331
332    # 勝率で並び替え
333    sorting_df["win_per"] = pd.to_numeric(sorting_df["win_per"], errors="coerce")
334    sorting_df["count"] = pd.to_numeric(sorting_df["count"], errors="coerce")
335    sorting_df = sorting_df.sort_values(by=["win_per", "count"], ascending=[False, False])
336    mtx_df = mtx_df.reindex(
337        index=list(sorting_df.index),
338        columns=list(sorting_df.index) + ["total"]
339    )
340
341    return mtx_df

対局対戦マトリックス表の作成

Returns:

pd.DataFrame: 集計結果