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))
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): メッセージデータ