libs.commands.ranking.ranking

libs/commands/ranking/ranking.py

  1"""
  2libs/commands/ranking/ranking.py
  3"""
  4
  5from typing import TYPE_CHECKING, cast
  6
  7import pandas as pd
  8
  9import libs.global_value as g
 10from libs.domain.datamodels import GameInfo
 11from libs.functions import message
 12from libs.types import StyleOptions
 13from libs.utils import formatter
 14
 15if TYPE_CHECKING:
 16    from integrations.protocols import MessageParserProtocol
 17
 18
 19def aggregation(m: "MessageParserProtocol") -> None:
 20    """
 21    ランキングデータを生成
 22
 23    Args:
 24        m (MessageParserProtocol): メッセージデータ
 25
 26    """
 27    # 情報ヘッダ
 28    if g.params.individual:  # 個人集計
 29        title = "ランキング"
 30    else:  # チーム集計
 31        title = "チームランキング"
 32
 33    # データ取得
 34    game_info = GameInfo()
 35    if not game_info.count:  # 検索結果が0件のとき
 36        m.set_headline(message.random_reply(m, "no_hits"), StyleOptions())
 37        m.status.result = False
 38        return
 39
 40    df = (
 41        pd.concat(
 42            [
 43                g.params.read_data("RESULTS_INFO").query("id==0").drop(columns=["id", "seat"]),
 44                g.params.read_data("RECORD_INFO").query("id==0").drop(columns=["id", "seat", "name"]),
 45            ],
 46            axis=1,
 47        )
 48        .drop(columns=["first_game", "last_game", "first_comment", "last_comment"])
 49        .query("count>=@g.params.stipulated")
 50    )
 51
 52    if df.empty:
 53        m.set_headline(message.random_reply(m, "no_target"), StyleOptions())
 54        m.status.result = False
 55        return
 56
 57    df["participation_rate"] = df["count"] / game_info.count  # ゲーム参加率
 58    df["avg_balance"] = df["score"] * 100 / df["count"]  # 平均収支
 59    df["rank1_rate"] = df["rank1"] / df["count"]  # トップ率
 60    df["top2_rate"] = (df["rank1"] + df["rank2"]) / df["count"]  # 連対率
 61    df["top3_rate"] = (df["rank1"] + df["rank2"] + df["rank3"]) / df["count"]  # ラス回避率
 62    df["flying_rate"] = df["flying"] / df["count"]  # トビ率
 63    df["yakuman_rate"] = df["yakuman"] / df["count"]  # 役満和了率
 64    if g.params.mode == 3:
 65        df["rank_distr"] = [f"{x.rank1}-{x.rank2}-{x.rank3}" for x in df.itertuples()]
 66    else:
 67        df["rank_distr"] = [f"{x.rank1}-{x.rank2}-{x.rank3}-{x.rank4}" for x in df.itertuples()]
 68
 69    if g.params.anonymous:
 70        mapping_dict = formatter.anonymous_mapping(df["name"].unique().tolist())
 71        df["name"] = df["name"].replace(mapping_dict)
 72
 73    # 集計
 74    data: dict[str, pd.DataFrame] = {}
 75
 76    data["ゲーム参加率"] = (
 77        pd.DataFrame(
 78            {
 79                "rank": df["participation_rate"].rank(ascending=False, method="dense").astype("int"),
 80                "name": df["name"],
 81                "participation_rate": [f"{x.participation_rate:.2%}" for x in df.itertuples()],
 82                "count": df["count"],
 83                "total_count": game_info.count,
 84            }
 85        )
 86        .sort_values("rank")
 87        .query("rank <= @g.params.ranked")
 88    )
 89    data["通算ポイント"] = (
 90        pd.DataFrame(
 91            {
 92                "rank": df["total_point"].rank(ascending=False, method="dense").astype("int"),
 93                "name": df["name"],
 94                "total_point": [f"{x.total_point:+.1f}pt".replace("-", "▲") for x in df.itertuples()],
 95                "count": df["count"],
 96            }
 97        )
 98        .sort_values(by=["rank", "count"], ascending=[True, False])
 99        .query("rank <= @g.params.ranked")
100    )
101    data["平均ポイント"] = (
102        pd.DataFrame(
103            {
104                "rank": df["avg_point"].rank(ascending=False, method="dense").astype("int"),
105                "name": df["name"],
106                "avg_point": [f"{x.avg_point:+.1f}pt".replace("-", "▲") for x in df.itertuples()],
107                "total_point": [f"{x.total_point:+.1f}pt".replace("-", "▲") for x in df.itertuples()],
108                "count": df["count"],
109            }
110        )
111        .sort_values(by=["rank", "count"], ascending=[True, False])
112        .query("rank <= @g.params.ranked")
113    )
114    data["平均収支"] = (
115        pd.DataFrame(
116            {
117                "rank": df["avg_balance"].rank(ascending=False, method="dense").astype("int"),
118                "name": df["name"],
119                "avg_balance": [f"{x.avg_balance:+.1f}点".replace("-", "▲") for x in df.itertuples()],
120                "rpoint_avg": [f"{cast(float, x.rpoint_avg) * 100:+.1f}点".replace("-", "▲") for x in df.itertuples()],
121                "count": df["count"],
122            }
123        )
124        .sort_values(by=["rank", "count"], ascending=[True, False])
125        .query("rank <= @g.params.ranked")
126    )
127    data["トップ率"] = (
128        pd.DataFrame(
129            {
130                "rank": df["rank1_rate"].rank(ascending=False, method="dense").astype("int"),
131                "name": df["name"],
132                "rank1_rate": [f"{x.rank1_rate:.2%}" for x in df.itertuples()],
133                "rank1": df["rank1"],
134                "count": df["count"],
135            }
136        )
137        .sort_values(by=["rank", "count"], ascending=[True, False])
138        .query("rank <= @g.params.ranked")
139    )
140    if g.params.mode == 3:
141        data["ラス回避率"] = (
142            pd.DataFrame(
143                {
144                    "rank": df["top2_rate"].rank(ascending=False, method="dense").astype("int"),
145                    "name": df["name"],
146                    "top2_rate": [f"{x.top2_rate:.2%}" for x in df.itertuples()],
147                    "top2": df["rank1"] + df["rank2"],
148                    "count": df["count"],
149                }
150            )
151            .sort_values(by=["rank", "count"], ascending=[True, False])
152            .query("rank <= @g.params.ranked")
153        )
154    else:
155        data["連対率"] = (
156            pd.DataFrame(
157                {
158                    "rank": df["top2_rate"].rank(ascending=False, method="dense").astype("int"),
159                    "name": df["name"],
160                    "top2_rate": [f"{x.top2_rate:.2%}" for x in df.itertuples()],
161                    "top2": df["rank1"] + df["rank2"],
162                    "count": df["count"],
163                }
164            )
165            .sort_values(by=["rank", "count"], ascending=[True, False])
166            .query("rank <= @g.params.ranked")
167        )
168        data["ラス回避率"] = (
169            pd.DataFrame(
170                {
171                    "rank": df["top3_rate"].rank(ascending=False, method="dense").astype("int"),
172                    "name": df["name"],
173                    "top3_rate": [f"{x.top3_rate:.2%}" for x in df.itertuples()],
174                    "top3": df["rank1"] + df["rank2"] + df["rank3"],
175                    "count": df["count"],
176                }
177            )
178            .sort_values(by=["rank", "count"], ascending=[True, False])
179            .query("rank <= @g.params.ranked")
180        )
181    data["トビ率"] = (
182        pd.DataFrame(
183            {
184                "rank": df["flying_rate"].rank(ascending=True, method="dense").astype("int"),
185                "name": df["name"],
186                "flying_rate": [f"{x.flying_rate:.2%}" for x in df.itertuples()],
187                "flying": df["flying"],
188                "count": df["count"],
189            }
190        )
191        .sort_values(by=["rank", "count"], ascending=[True, False])
192        .query("rank <= @g.params.ranked")
193    )
194    data["平均順位"] = (
195        pd.DataFrame(
196            {
197                "rank": df["rank_avg"].rank(ascending=True, method="dense").astype("int"),
198                "name": df["name"],
199                "rank_avg": [f"{x.rank_avg:.2f}" for x in df.itertuples()],
200                "rank_distr": df["rank_distr"],
201                "count": df["count"],
202            }
203        )
204        .sort_values(by=["rank", "count"], ascending=[True, False])
205        .query("rank <= @g.params.ranked")
206    )
207    data["役満和了率"] = (
208        pd.DataFrame(
209            {
210                "rank": df["yakuman_rate"].rank(ascending=False, method="dense").astype("int"),
211                "name": df["name"],
212                "yakuman_rate": [f"{x.yakuman_rate:.2%}" for x in df.itertuples()],
213                "yakuman": df["yakuman"],
214                "count": df["count"],
215            }
216        )
217        .sort_values(by=["rank", "count"], ascending=[True, False])
218        .query("rank <= @g.params.ranked and yakuman > 0")
219    )
220    data["最大素点"] = (
221        pd.DataFrame(
222            {
223                "rank": df["rpoint_max"].rank(ascending=False, method="dense").astype("int"),
224                "name": df["name"],
225                "rpoint_max": [f"{cast(float, x.rpoint_max) * 100}点".replace("-", "▲") for x in df.itertuples()],
226                "point_max": [f"{x.point_max:+.1f}pt".replace("-", "▲") for x in df.itertuples()],
227                "count": df["count"],
228            }
229        )
230        .sort_values(by=["rank", "count"], ascending=[True, False])
231        .query("rank <= @g.params.ranked")
232    )
233    data["連続トップ"] = (
234        pd.DataFrame(
235            {
236                "rank": df["top1_max"].rank(ascending=False, method="dense").astype("int"),
237                "name": df["name"],
238                "top1_max": df["top1_max"],
239                "count": df["count"],
240            }
241        )
242        .sort_values(by=["rank", "count"], ascending=[True, False])
243        .query("rank <= @g.params.ranked and top1_max > 1")
244    )
245    if g.params.mode == 3:
246        data["連続ラス回避"] = (
247            pd.DataFrame(
248                {
249                    "rank": df["top2_max"].rank(ascending=False, method="dense").astype("int"),
250                    "name": df["name"],
251                    "top2_max": df["top2_max"],
252                    "count": df["count"],
253                }
254            )
255            .sort_values(by=["rank", "count"], ascending=[True, False])
256            .query("rank <= @g.params.ranked and top2_max > 1")
257        )
258    else:
259        data["連続連対"] = (
260            pd.DataFrame(
261                {
262                    "rank": df["top2_max"].rank(ascending=False, method="dense").astype("int"),
263                    "name": df["name"],
264                    "top2_max": df["top2_max"],
265                    "count": df["count"],
266                }
267            )
268            .sort_values(by=["rank", "count"], ascending=[True, False])
269            .query("rank <= @g.params.ranked and top2_max > 1")
270        )
271        data["連続ラス回避"] = (
272            pd.DataFrame(
273                {
274                    "rank": df["top3_max"].rank(ascending=False, method="dense").astype("int"),
275                    "name": df["name"],
276                    "top3_max": df["top3_max"],
277                    "count": df["count"],
278                }
279            )
280            .sort_values(by=["rank", "count"], ascending=[True, False])
281            .query("rank <= @g.params.ranked and top3_max > 1")
282        )
283
284    # 項目整理
285    if g.cfg.rule.dropitems(g.params.rule_version) & g.cfg.dropitems.flying or g.params.ignore_flying:
286        data.pop("トビ率")
287    if g.cfg.rule.dropitems(g.params.rule_version) & g.cfg.dropitems.yakuman:
288        data.pop("役満和了率")
289
290    for msg, df_data in data.items():
291        if msg in g.cfg.rule.dropitems(g.params.rule_version):  # 非表示項目
292            continue
293        if df_data.empty:  # 対象者なし
294            continue
295        m.set_message(
296            df_data,
297            StyleOptions(
298                title=msg,
299                data_kind=StyleOptions.DataKind.RANKING,
300                rename_type=StyleOptions.RenameType.SHORT,
301                codeblock=True,
302                show_index=False,
303            ),
304        )
305
306    m.set_headline(message.header(game_info, m, "", 1), StyleOptions(title=title))
def aggregation(m: integrations.protocols.MessageParserProtocol) -> None:
 20def aggregation(m: "MessageParserProtocol") -> None:
 21    """
 22    ランキングデータを生成
 23
 24    Args:
 25        m (MessageParserProtocol): メッセージデータ
 26
 27    """
 28    # 情報ヘッダ
 29    if g.params.individual:  # 個人集計
 30        title = "ランキング"
 31    else:  # チーム集計
 32        title = "チームランキング"
 33
 34    # データ取得
 35    game_info = GameInfo()
 36    if not game_info.count:  # 検索結果が0件のとき
 37        m.set_headline(message.random_reply(m, "no_hits"), StyleOptions())
 38        m.status.result = False
 39        return
 40
 41    df = (
 42        pd.concat(
 43            [
 44                g.params.read_data("RESULTS_INFO").query("id==0").drop(columns=["id", "seat"]),
 45                g.params.read_data("RECORD_INFO").query("id==0").drop(columns=["id", "seat", "name"]),
 46            ],
 47            axis=1,
 48        )
 49        .drop(columns=["first_game", "last_game", "first_comment", "last_comment"])
 50        .query("count>=@g.params.stipulated")
 51    )
 52
 53    if df.empty:
 54        m.set_headline(message.random_reply(m, "no_target"), StyleOptions())
 55        m.status.result = False
 56        return
 57
 58    df["participation_rate"] = df["count"] / game_info.count  # ゲーム参加率
 59    df["avg_balance"] = df["score"] * 100 / df["count"]  # 平均収支
 60    df["rank1_rate"] = df["rank1"] / df["count"]  # トップ率
 61    df["top2_rate"] = (df["rank1"] + df["rank2"]) / df["count"]  # 連対率
 62    df["top3_rate"] = (df["rank1"] + df["rank2"] + df["rank3"]) / df["count"]  # ラス回避率
 63    df["flying_rate"] = df["flying"] / df["count"]  # トビ率
 64    df["yakuman_rate"] = df["yakuman"] / df["count"]  # 役満和了率
 65    if g.params.mode == 3:
 66        df["rank_distr"] = [f"{x.rank1}-{x.rank2}-{x.rank3}" for x in df.itertuples()]
 67    else:
 68        df["rank_distr"] = [f"{x.rank1}-{x.rank2}-{x.rank3}-{x.rank4}" for x in df.itertuples()]
 69
 70    if g.params.anonymous:
 71        mapping_dict = formatter.anonymous_mapping(df["name"].unique().tolist())
 72        df["name"] = df["name"].replace(mapping_dict)
 73
 74    # 集計
 75    data: dict[str, pd.DataFrame] = {}
 76
 77    data["ゲーム参加率"] = (
 78        pd.DataFrame(
 79            {
 80                "rank": df["participation_rate"].rank(ascending=False, method="dense").astype("int"),
 81                "name": df["name"],
 82                "participation_rate": [f"{x.participation_rate:.2%}" for x in df.itertuples()],
 83                "count": df["count"],
 84                "total_count": game_info.count,
 85            }
 86        )
 87        .sort_values("rank")
 88        .query("rank <= @g.params.ranked")
 89    )
 90    data["通算ポイント"] = (
 91        pd.DataFrame(
 92            {
 93                "rank": df["total_point"].rank(ascending=False, method="dense").astype("int"),
 94                "name": df["name"],
 95                "total_point": [f"{x.total_point:+.1f}pt".replace("-", "▲") for x in df.itertuples()],
 96                "count": df["count"],
 97            }
 98        )
 99        .sort_values(by=["rank", "count"], ascending=[True, False])
100        .query("rank <= @g.params.ranked")
101    )
102    data["平均ポイント"] = (
103        pd.DataFrame(
104            {
105                "rank": df["avg_point"].rank(ascending=False, method="dense").astype("int"),
106                "name": df["name"],
107                "avg_point": [f"{x.avg_point:+.1f}pt".replace("-", "▲") for x in df.itertuples()],
108                "total_point": [f"{x.total_point:+.1f}pt".replace("-", "▲") for x in df.itertuples()],
109                "count": df["count"],
110            }
111        )
112        .sort_values(by=["rank", "count"], ascending=[True, False])
113        .query("rank <= @g.params.ranked")
114    )
115    data["平均収支"] = (
116        pd.DataFrame(
117            {
118                "rank": df["avg_balance"].rank(ascending=False, method="dense").astype("int"),
119                "name": df["name"],
120                "avg_balance": [f"{x.avg_balance:+.1f}点".replace("-", "▲") for x in df.itertuples()],
121                "rpoint_avg": [f"{cast(float, x.rpoint_avg) * 100:+.1f}点".replace("-", "▲") for x in df.itertuples()],
122                "count": df["count"],
123            }
124        )
125        .sort_values(by=["rank", "count"], ascending=[True, False])
126        .query("rank <= @g.params.ranked")
127    )
128    data["トップ率"] = (
129        pd.DataFrame(
130            {
131                "rank": df["rank1_rate"].rank(ascending=False, method="dense").astype("int"),
132                "name": df["name"],
133                "rank1_rate": [f"{x.rank1_rate:.2%}" for x in df.itertuples()],
134                "rank1": df["rank1"],
135                "count": df["count"],
136            }
137        )
138        .sort_values(by=["rank", "count"], ascending=[True, False])
139        .query("rank <= @g.params.ranked")
140    )
141    if g.params.mode == 3:
142        data["ラス回避率"] = (
143            pd.DataFrame(
144                {
145                    "rank": df["top2_rate"].rank(ascending=False, method="dense").astype("int"),
146                    "name": df["name"],
147                    "top2_rate": [f"{x.top2_rate:.2%}" for x in df.itertuples()],
148                    "top2": df["rank1"] + df["rank2"],
149                    "count": df["count"],
150                }
151            )
152            .sort_values(by=["rank", "count"], ascending=[True, False])
153            .query("rank <= @g.params.ranked")
154        )
155    else:
156        data["連対率"] = (
157            pd.DataFrame(
158                {
159                    "rank": df["top2_rate"].rank(ascending=False, method="dense").astype("int"),
160                    "name": df["name"],
161                    "top2_rate": [f"{x.top2_rate:.2%}" for x in df.itertuples()],
162                    "top2": df["rank1"] + df["rank2"],
163                    "count": df["count"],
164                }
165            )
166            .sort_values(by=["rank", "count"], ascending=[True, False])
167            .query("rank <= @g.params.ranked")
168        )
169        data["ラス回避率"] = (
170            pd.DataFrame(
171                {
172                    "rank": df["top3_rate"].rank(ascending=False, method="dense").astype("int"),
173                    "name": df["name"],
174                    "top3_rate": [f"{x.top3_rate:.2%}" for x in df.itertuples()],
175                    "top3": df["rank1"] + df["rank2"] + df["rank3"],
176                    "count": df["count"],
177                }
178            )
179            .sort_values(by=["rank", "count"], ascending=[True, False])
180            .query("rank <= @g.params.ranked")
181        )
182    data["トビ率"] = (
183        pd.DataFrame(
184            {
185                "rank": df["flying_rate"].rank(ascending=True, method="dense").astype("int"),
186                "name": df["name"],
187                "flying_rate": [f"{x.flying_rate:.2%}" for x in df.itertuples()],
188                "flying": df["flying"],
189                "count": df["count"],
190            }
191        )
192        .sort_values(by=["rank", "count"], ascending=[True, False])
193        .query("rank <= @g.params.ranked")
194    )
195    data["平均順位"] = (
196        pd.DataFrame(
197            {
198                "rank": df["rank_avg"].rank(ascending=True, method="dense").astype("int"),
199                "name": df["name"],
200                "rank_avg": [f"{x.rank_avg:.2f}" for x in df.itertuples()],
201                "rank_distr": df["rank_distr"],
202                "count": df["count"],
203            }
204        )
205        .sort_values(by=["rank", "count"], ascending=[True, False])
206        .query("rank <= @g.params.ranked")
207    )
208    data["役満和了率"] = (
209        pd.DataFrame(
210            {
211                "rank": df["yakuman_rate"].rank(ascending=False, method="dense").astype("int"),
212                "name": df["name"],
213                "yakuman_rate": [f"{x.yakuman_rate:.2%}" for x in df.itertuples()],
214                "yakuman": df["yakuman"],
215                "count": df["count"],
216            }
217        )
218        .sort_values(by=["rank", "count"], ascending=[True, False])
219        .query("rank <= @g.params.ranked and yakuman > 0")
220    )
221    data["最大素点"] = (
222        pd.DataFrame(
223            {
224                "rank": df["rpoint_max"].rank(ascending=False, method="dense").astype("int"),
225                "name": df["name"],
226                "rpoint_max": [f"{cast(float, x.rpoint_max) * 100}点".replace("-", "▲") for x in df.itertuples()],
227                "point_max": [f"{x.point_max:+.1f}pt".replace("-", "▲") for x in df.itertuples()],
228                "count": df["count"],
229            }
230        )
231        .sort_values(by=["rank", "count"], ascending=[True, False])
232        .query("rank <= @g.params.ranked")
233    )
234    data["連続トップ"] = (
235        pd.DataFrame(
236            {
237                "rank": df["top1_max"].rank(ascending=False, method="dense").astype("int"),
238                "name": df["name"],
239                "top1_max": df["top1_max"],
240                "count": df["count"],
241            }
242        )
243        .sort_values(by=["rank", "count"], ascending=[True, False])
244        .query("rank <= @g.params.ranked and top1_max > 1")
245    )
246    if g.params.mode == 3:
247        data["連続ラス回避"] = (
248            pd.DataFrame(
249                {
250                    "rank": df["top2_max"].rank(ascending=False, method="dense").astype("int"),
251                    "name": df["name"],
252                    "top2_max": df["top2_max"],
253                    "count": df["count"],
254                }
255            )
256            .sort_values(by=["rank", "count"], ascending=[True, False])
257            .query("rank <= @g.params.ranked and top2_max > 1")
258        )
259    else:
260        data["連続連対"] = (
261            pd.DataFrame(
262                {
263                    "rank": df["top2_max"].rank(ascending=False, method="dense").astype("int"),
264                    "name": df["name"],
265                    "top2_max": df["top2_max"],
266                    "count": df["count"],
267                }
268            )
269            .sort_values(by=["rank", "count"], ascending=[True, False])
270            .query("rank <= @g.params.ranked and top2_max > 1")
271        )
272        data["連続ラス回避"] = (
273            pd.DataFrame(
274                {
275                    "rank": df["top3_max"].rank(ascending=False, method="dense").astype("int"),
276                    "name": df["name"],
277                    "top3_max": df["top3_max"],
278                    "count": df["count"],
279                }
280            )
281            .sort_values(by=["rank", "count"], ascending=[True, False])
282            .query("rank <= @g.params.ranked and top3_max > 1")
283        )
284
285    # 項目整理
286    if g.cfg.rule.dropitems(g.params.rule_version) & g.cfg.dropitems.flying or g.params.ignore_flying:
287        data.pop("トビ率")
288    if g.cfg.rule.dropitems(g.params.rule_version) & g.cfg.dropitems.yakuman:
289        data.pop("役満和了率")
290
291    for msg, df_data in data.items():
292        if msg in g.cfg.rule.dropitems(g.params.rule_version):  # 非表示項目
293            continue
294        if df_data.empty:  # 対象者なし
295            continue
296        m.set_message(
297            df_data,
298            StyleOptions(
299                title=msg,
300                data_kind=StyleOptions.DataKind.RANKING,
301                rename_type=StyleOptions.RenameType.SHORT,
302                codeblock=True,
303                show_index=False,
304            ),
305        )
306
307    m.set_headline(message.header(game_info, m, "", 1), StyleOptions(title=title))

ランキングデータを生成

Arguments:
  • m (MessageParserProtocol): メッセージデータ