libs.commands.ranking.ranking

libs/commands/results/ranking.py

  1"""
  2libs/commands/results/ranking.py
  3"""
  4
  5import re
  6
  7import pandas as pd
  8from tabulate import tabulate
  9
 10import libs.global_value as g
 11from cls.types import GameInfoDict
 12from libs.data import aggregate, loader
 13from libs.functions import message
 14from libs.utils import formatter
 15
 16
 17def aggregation() -> tuple[str, dict]:
 18    """ランキングデータを生成
 19
 20    Returns:
 21        tuple[str, dict]: 集計結果
 22        - str: ランキングの集計情報
 23        - dict: 各ランキングの情報
 24    """
 25
 26    # 情報ヘッダ
 27    if g.params.get("individual"):  # 個人集計
 28        msg = "\n*【ランキング】*\n"
 29    else:  # チーム集計
 30        msg = "\n*【チームランキング】*\n"
 31
 32    # データ取得
 33    game_info: GameInfoDict = aggregate.game_info()
 34    if not game_info["game_count"]:  # 検索結果が0件のとき
 35        msg += "\t" + message.reply(message="no_hits")
 36        return (msg, {})
 37
 38    result_df = loader.read_data("ranking/aggregate.sql")
 39    if result_df.empty:
 40        msg += "\t" + message.reply(message="no_target")
 41        return (msg, {})
 42
 43    df = pd.merge(
 44        result_df, aggregate.ranking_record(),
 45        on=["name", "name"],
 46        suffixes=["", "_x"]
 47    )
 48
 49    if g.params.get("anonymous"):
 50        mapping_dict = formatter.anonymous_mapping(df["name"].unique().tolist())
 51        df["name"] = df["name"].replace(mapping_dict)
 52
 53    # 集計
 54    data: dict = {}
 55
 56    # ゲーム参加率
 57    df["participation_rate"] = df["game_count"] / game_info["game_count"]
 58    df["rank"] = df["participation_rate"].rank(ascending=False, method="dense").astype("int")
 59    df["disp"] = df.apply(lambda row: f"<>{row["participation_rate"]:>7.2%} ({row["game_count"]:3d}G / {game_info["game_count"]:4d}G)", axis=1)
 60    data["ゲーム参加率"] = table_conversion(df)
 61
 62    # 通算ポイント
 63    df["rank"] = df["point_sum"].rank(ascending=False, method="dense").astype("int")
 64    df["disp"] = df.apply(lambda row: f"<>{row["point_sum"]:>+7.1f}pt ({row["game_count"]:3d}G)", axis=1)
 65    data["通算ポイント"] = table_conversion(df)
 66
 67    # 平均ポイント
 68    df["rank"] = df["point_avg"].rank(ascending=False, method="dense").astype("int")
 69    df["disp"] = df.apply(lambda row: f"<>{row["point_avg"]:>+7.1f}pt ({row["point_sum"]:>+7.1f}pt / {row["game_count"]:3d}G)", axis=1)
 70    data["平均ポイント"] = table_conversion(df)
 71
 72    # 平均収支
 73    df["rank"] = df["rpoint_avg"].rank(ascending=False, method="dense").astype("int")
 74    df["disp"] = df.apply(lambda row: f"<>{row["rpoint_avg"] - 25000:>6.0f}点 ({row["rpoint_avg"]:>6.0f}点 / {row["game_count"]:3d}G)", axis=1)
 75    data["平均収支"] = table_conversion(df)
 76
 77    # トップ率
 78    df["rank"] = df["rank1_rate"].rank(ascending=False, method="dense").astype("int")
 79    df["disp"] = df.apply(lambda row: f"<>{row["rank1_rate"]:>7.2%} ({row["rank1"]:3d} / {row["game_count"]:3d}G)", axis=1)
 80    data["トップ率"] = table_conversion(df)
 81
 82    # 連対率
 83    df["rank"] = df["top2_rate"].rank(ascending=False, method="dense").astype("int")
 84    df["disp"] = df.apply(lambda row: f"<>{row["top2_rate"]:>7.2%} ({row["top2"]:3d} / {row["game_count"]:3d}G)", axis=1)
 85    data["連対率"] = table_conversion(df)
 86
 87    # ラス回避率
 88    df["rank"] = df["top3_rate"].rank(ascending=False, method="dense").astype("int")
 89    df["disp"] = df.apply(lambda row: f"<>{row["top3_rate"]:>7.2%} ({row["top3"]:3d} / {row["game_count"]:3d}G)", axis=1)
 90    data["ラス回避率"] = table_conversion(df)
 91
 92    # トビ率
 93    df["rank"] = df["flying_rate"].rank(ascending=True, method="dense").astype("int")
 94    df["disp"] = df.apply(lambda row: f"<>{row["flying_rate"]:>7.2%} ({row["flying"]:3d} / {row["game_count"]:3d}G)", axis=1)
 95    data["トビ率"] = table_conversion(df)
 96
 97    # 平均順位
 98    df["rank"] = df["rank_avg"].rank(ascending=True, method="dense").astype("int")
 99    df["disp"] = df.apply(lambda row: f"<>{row["rank_avg"]:>4.2f} ({row["rank_dist"]})", axis=1)
100    data["平均順位"] = table_conversion(df)
101
102    # 役満和了率
103    df["rank"] = df["gs_rate"].rank(ascending=False, method="dense").astype("int")
104    df["disp"] = df.apply(lambda row: f"<>{row["gs_rate"]:>7.2%} ({row["gs_count"]:3d} / {row["game_count"]:3d}G)", axis=1)
105    data["役満和了率"] = table_conversion(df, ["gs_count", 1])
106
107    # 最大素点
108    df["rank"] = df["rpoint_max"].rank(ascending=False, method="dense").astype("int")
109    df["disp"] = df.apply(lambda row: f"<>{row["rpoint_max"]:>6.0f}点 ({row["point_max"]:>+7.1f}pt)", axis=1)
110    data["最大素点"] = table_conversion(df)
111
112    # 連続トップ
113    df["rank"] = df["c_top"].rank(ascending=False, method="dense").astype("int")
114    df["disp"] = df.apply(lambda row: f"<>{row["c_top"]:>2d}連続 ({row["game_count"]:3d}G)", axis=1)
115    data["連続トップ"] = table_conversion(df, ["c_top", 2])
116
117    # 連続連対
118    df["rank"] = df["c_top2"].rank(ascending=False, method="dense").astype("int")
119    df["disp"] = df.apply(lambda row: f"<>{row["c_top2"]:>2d}連続 ({row["game_count"]:3d}G)", axis=1)
120    data["連続連対"] = table_conversion(df, ["c_top2", 2])
121
122    # 連続ラス回避
123    df["rank"] = df["c_top3"].rank(ascending=False, method="dense").astype("int")
124    df["disp"] = df.apply(lambda row: f"<>{row["c_top3"]:>2d}連続 ({row["game_count"]:3d}G)", axis=1)
125    data["連続ラス回避"] = table_conversion(df, ["c_top3", 2])
126
127    # 表示
128    msg += message.header(game_info, "", 1)
129
130    for key in list(data.keys()):
131        if key in g.cfg.dropitems.ranking:  # 非表示項目
132            data.pop(key)
133            continue
134
135        if key in data:  # 対象者がいなければ項目を削除
136            if not data[key]:
137                data.pop(key)
138                continue
139
140        data[key] = f"*{key}*\n" + data[key]
141
142    return (msg, data)
143
144
145def table_conversion(df: pd.DataFrame, threshold: list | None = None) -> str:
146    """テーブル変換
147
148    Args:
149        df (pd.DataFrame): 変換対象データ
150        threshold (list | None, optional): 非表示にする閾値. Defaults to None.
151
152    Returns:
153        str: 作成したテーブル
154    """
155
156    if isinstance(threshold, list):
157        df = df.query(f"{threshold[0]} >= @threshold[1]").copy()
158
159    if df.empty:
160        return ""
161
162    df.sort_values(by=["rank", "game_count"], ascending=[True, False], inplace=True)
163    df = df.query("rank <= @g.cfg.ranking.ranked")
164    tbl = tabulate(df.filter(items=["rank", "name", "disp"]).values)
165    tbl = re.sub(r"( *[0-9]+)\s(.*)<>(.*)", r"\1:\2\3", tbl)
166    tbl = "\n".join(tbl.splitlines()[1:-1]).replace(" -", "▲")
167    tbl = f"\n```\n{tbl}\n```\n"
168
169    return tbl
def aggregation() -> tuple[str, dict]:
 18def aggregation() -> tuple[str, dict]:
 19    """ランキングデータを生成
 20
 21    Returns:
 22        tuple[str, dict]: 集計結果
 23        - str: ランキングの集計情報
 24        - dict: 各ランキングの情報
 25    """
 26
 27    # 情報ヘッダ
 28    if g.params.get("individual"):  # 個人集計
 29        msg = "\n*【ランキング】*\n"
 30    else:  # チーム集計
 31        msg = "\n*【チームランキング】*\n"
 32
 33    # データ取得
 34    game_info: GameInfoDict = aggregate.game_info()
 35    if not game_info["game_count"]:  # 検索結果が0件のとき
 36        msg += "\t" + message.reply(message="no_hits")
 37        return (msg, {})
 38
 39    result_df = loader.read_data("ranking/aggregate.sql")
 40    if result_df.empty:
 41        msg += "\t" + message.reply(message="no_target")
 42        return (msg, {})
 43
 44    df = pd.merge(
 45        result_df, aggregate.ranking_record(),
 46        on=["name", "name"],
 47        suffixes=["", "_x"]
 48    )
 49
 50    if g.params.get("anonymous"):
 51        mapping_dict = formatter.anonymous_mapping(df["name"].unique().tolist())
 52        df["name"] = df["name"].replace(mapping_dict)
 53
 54    # 集計
 55    data: dict = {}
 56
 57    # ゲーム参加率
 58    df["participation_rate"] = df["game_count"] / game_info["game_count"]
 59    df["rank"] = df["participation_rate"].rank(ascending=False, method="dense").astype("int")
 60    df["disp"] = df.apply(lambda row: f"<>{row["participation_rate"]:>7.2%} ({row["game_count"]:3d}G / {game_info["game_count"]:4d}G)", axis=1)
 61    data["ゲーム参加率"] = table_conversion(df)
 62
 63    # 通算ポイント
 64    df["rank"] = df["point_sum"].rank(ascending=False, method="dense").astype("int")
 65    df["disp"] = df.apply(lambda row: f"<>{row["point_sum"]:>+7.1f}pt ({row["game_count"]:3d}G)", axis=1)
 66    data["通算ポイント"] = table_conversion(df)
 67
 68    # 平均ポイント
 69    df["rank"] = df["point_avg"].rank(ascending=False, method="dense").astype("int")
 70    df["disp"] = df.apply(lambda row: f"<>{row["point_avg"]:>+7.1f}pt ({row["point_sum"]:>+7.1f}pt / {row["game_count"]:3d}G)", axis=1)
 71    data["平均ポイント"] = table_conversion(df)
 72
 73    # 平均収支
 74    df["rank"] = df["rpoint_avg"].rank(ascending=False, method="dense").astype("int")
 75    df["disp"] = df.apply(lambda row: f"<>{row["rpoint_avg"] - 25000:>6.0f}点 ({row["rpoint_avg"]:>6.0f}点 / {row["game_count"]:3d}G)", axis=1)
 76    data["平均収支"] = table_conversion(df)
 77
 78    # トップ率
 79    df["rank"] = df["rank1_rate"].rank(ascending=False, method="dense").astype("int")
 80    df["disp"] = df.apply(lambda row: f"<>{row["rank1_rate"]:>7.2%} ({row["rank1"]:3d} / {row["game_count"]:3d}G)", axis=1)
 81    data["トップ率"] = table_conversion(df)
 82
 83    # 連対率
 84    df["rank"] = df["top2_rate"].rank(ascending=False, method="dense").astype("int")
 85    df["disp"] = df.apply(lambda row: f"<>{row["top2_rate"]:>7.2%} ({row["top2"]:3d} / {row["game_count"]:3d}G)", axis=1)
 86    data["連対率"] = table_conversion(df)
 87
 88    # ラス回避率
 89    df["rank"] = df["top3_rate"].rank(ascending=False, method="dense").astype("int")
 90    df["disp"] = df.apply(lambda row: f"<>{row["top3_rate"]:>7.2%} ({row["top3"]:3d} / {row["game_count"]:3d}G)", axis=1)
 91    data["ラス回避率"] = table_conversion(df)
 92
 93    # トビ率
 94    df["rank"] = df["flying_rate"].rank(ascending=True, method="dense").astype("int")
 95    df["disp"] = df.apply(lambda row: f"<>{row["flying_rate"]:>7.2%} ({row["flying"]:3d} / {row["game_count"]:3d}G)", axis=1)
 96    data["トビ率"] = table_conversion(df)
 97
 98    # 平均順位
 99    df["rank"] = df["rank_avg"].rank(ascending=True, method="dense").astype("int")
100    df["disp"] = df.apply(lambda row: f"<>{row["rank_avg"]:>4.2f} ({row["rank_dist"]})", axis=1)
101    data["平均順位"] = table_conversion(df)
102
103    # 役満和了率
104    df["rank"] = df["gs_rate"].rank(ascending=False, method="dense").astype("int")
105    df["disp"] = df.apply(lambda row: f"<>{row["gs_rate"]:>7.2%} ({row["gs_count"]:3d} / {row["game_count"]:3d}G)", axis=1)
106    data["役満和了率"] = table_conversion(df, ["gs_count", 1])
107
108    # 最大素点
109    df["rank"] = df["rpoint_max"].rank(ascending=False, method="dense").astype("int")
110    df["disp"] = df.apply(lambda row: f"<>{row["rpoint_max"]:>6.0f}点 ({row["point_max"]:>+7.1f}pt)", axis=1)
111    data["最大素点"] = table_conversion(df)
112
113    # 連続トップ
114    df["rank"] = df["c_top"].rank(ascending=False, method="dense").astype("int")
115    df["disp"] = df.apply(lambda row: f"<>{row["c_top"]:>2d}連続 ({row["game_count"]:3d}G)", axis=1)
116    data["連続トップ"] = table_conversion(df, ["c_top", 2])
117
118    # 連続連対
119    df["rank"] = df["c_top2"].rank(ascending=False, method="dense").astype("int")
120    df["disp"] = df.apply(lambda row: f"<>{row["c_top2"]:>2d}連続 ({row["game_count"]:3d}G)", axis=1)
121    data["連続連対"] = table_conversion(df, ["c_top2", 2])
122
123    # 連続ラス回避
124    df["rank"] = df["c_top3"].rank(ascending=False, method="dense").astype("int")
125    df["disp"] = df.apply(lambda row: f"<>{row["c_top3"]:>2d}連続 ({row["game_count"]:3d}G)", axis=1)
126    data["連続ラス回避"] = table_conversion(df, ["c_top3", 2])
127
128    # 表示
129    msg += message.header(game_info, "", 1)
130
131    for key in list(data.keys()):
132        if key in g.cfg.dropitems.ranking:  # 非表示項目
133            data.pop(key)
134            continue
135
136        if key in data:  # 対象者がいなければ項目を削除
137            if not data[key]:
138                data.pop(key)
139                continue
140
141        data[key] = f"*{key}*\n" + data[key]
142
143    return (msg, data)

ランキングデータを生成

Returns:

tuple[str, dict]: 集計結果

  • str: ランキングの集計情報
  • dict: 各ランキングの情報
def table_conversion(df: pandas.core.frame.DataFrame, threshold: list | None = None) -> str:
146def table_conversion(df: pd.DataFrame, threshold: list | None = None) -> str:
147    """テーブル変換
148
149    Args:
150        df (pd.DataFrame): 変換対象データ
151        threshold (list | None, optional): 非表示にする閾値. Defaults to None.
152
153    Returns:
154        str: 作成したテーブル
155    """
156
157    if isinstance(threshold, list):
158        df = df.query(f"{threshold[0]} >= @threshold[1]").copy()
159
160    if df.empty:
161        return ""
162
163    df.sort_values(by=["rank", "game_count"], ascending=[True, False], inplace=True)
164    df = df.query("rank <= @g.cfg.ranking.ranked")
165    tbl = tabulate(df.filter(items=["rank", "name", "disp"]).values)
166    tbl = re.sub(r"( *[0-9]+)\s(.*)<>(.*)", r"\1:\2\3", tbl)
167    tbl = "\n".join(tbl.splitlines()[1:-1]).replace(" -", "▲")
168    tbl = f"\n```\n{tbl}\n```\n"
169
170    return tbl

テーブル変換

Arguments:
  • df (pd.DataFrame): 変換対象データ
  • threshold (list | None, optional): 非表示にする閾値. Defaults to None.
Returns:

str: 作成したテーブル