libs.commands.ranking.rating
libs/commands/ranking/rating.py
1""" 2libs/commands/ranking/rating.py 3""" 4 5from typing import TYPE_CHECKING 6 7import pandas as pd 8 9import libs.global_value as g 10from libs.domain import aggregate 11from libs.domain.datamodels import GameInfo 12from libs.functions import message 13from libs.functions.compose import badge 14from libs.types import CommandType, StyleOptions 15from libs.utils import converter, formatter 16 17if TYPE_CHECKING: 18 from integrations.protocols import MessageParserProtocol 19 from libs.types import MessageType 20 21 22def aggregation(m: "MessageParserProtocol") -> None: 23 """ 24 レーティングを集計して返す 25 26 Args: 27 m (MessageParserProtocol): メッセージデータ 28 29 """ 30 m.status.command_type = CommandType.RATING # 更新 31 32 # 情報ヘッダ 33 title: str = "レーティング" 34 add_text: str = "" 35 36 if g.params.mode == 3 or g.params.target_mode == 3: # todo: 未実装 37 m.set_headline(message.random_reply(m, "not_implemented"), StyleOptions(title=title)) 38 m.status.result = False 39 return 40 41 # データ収集 42 game_info = GameInfo() 43 44 if not game_info.count: # 検索結果が0件のとき 45 m.set_headline(message.random_reply(m, "no_hits"), StyleOptions()) 46 m.status.result = False 47 return 48 49 df_results = g.params.read_data("RANKING_RESULTS").set_index("name") 50 df_ratings = aggregate.calculation_rating() 51 52 # 最終的なレーティング 53 final = df_ratings.ffill().tail(1).transpose() 54 final.columns = ["rate"] 55 final["name"] = final.index 56 57 df = pd.merge(df_results, final, on=["name"]).sort_values(by="rate", ascending=False) 58 df = df.query("count >= @g.params.stipulated") # 足切り 59 df["rank"] = 0 # 順位表示用カラム 60 61 # 集計対象外データの削除 62 if g.params.unregistered_replace: # 個人戦 63 for player in df.itertuples(): 64 if player.name not in g.cfg.member.lists: 65 df = df.drop(player.Index) 66 67 if not g.params.individual: # チーム戦 68 df = df.query("name != '未所属'") 69 70 # 順位偏差 / 得点偏差 71 df["point_dev"] = round((df["rpoint_avg"] - df["rpoint_avg"].mean()) / df["rpoint_avg"].std(ddof=0) * 10 + 50, 1) 72 df["rank_dev"] = round((df["rank_avg"] - df["rank_avg"].mean()) / df["rank_avg"].std(ddof=0) * -10 + 50, 1) 73 74 # 段位 75 if g.adapter.conf.badge_grade: 76 for idx in df.index: 77 name = str(df.at[idx, "name"]).replace(f"({g.cfg.setting.guest_mark})", "") 78 df.at[idx, "grade"] = badge.grade(name, False) 79 80 # 表示 81 if g.params.anonymous: 82 mapping_dict = formatter.anonymous_mapping(df["name"].unique().tolist()) 83 df["name"] = df["name"].replace(mapping_dict) 84 85 if df.empty: 86 m.set_headline(message.random_reply(m, "no_target"), StyleOptions()) 87 m.status.result = False 88 return 89 90 df["rank"] = df["rate"].rank(ascending=False, method="dense").astype("int") 91 df["rate"] = df["rate"].map(lambda v: round(v, 1)) 92 df = df.query("rank <= @g.params.ranked").filter( 93 items=["rank", "name", "rate", "rank_distr", "rank_avg", "rank_dev", "rpoint_avg", "point_dev", "grade"], 94 ) 95 96 # 非表示項目を削除 97 df = formatter.df_drop(df, list(g.cfg.rule.dropitems(g.params.rule_version))) 98 99 m.set_headline(message.header(game_info, m, add_text, 1), StyleOptions(title=title)) 100 options: StyleOptions = StyleOptions( 101 title=title, 102 data_kind=StyleOptions.DataKind.RATING, 103 rename_type=StyleOptions.RenameType.SHORT, 104 base_name="rating", 105 format_type="default", 106 summarize=False, 107 codeblock=True, 108 ) 109 110 data: "MessageType" 111 match g.params.format.lower(): 112 case "csv": 113 options.format_type = "csv" 114 data = converter.save_output(df, options, m.post.headline) 115 case "text" | "txt": 116 options.format_type = "txt" 117 data = converter.save_output(df, options, m.post.headline) 118 case _: 119 options.key_title = False 120 data = df 121 122 m.set_message(data, options)
23def aggregation(m: "MessageParserProtocol") -> None: 24 """ 25 レーティングを集計して返す 26 27 Args: 28 m (MessageParserProtocol): メッセージデータ 29 30 """ 31 m.status.command_type = CommandType.RATING # 更新 32 33 # 情報ヘッダ 34 title: str = "レーティング" 35 add_text: str = "" 36 37 if g.params.mode == 3 or g.params.target_mode == 3: # todo: 未実装 38 m.set_headline(message.random_reply(m, "not_implemented"), StyleOptions(title=title)) 39 m.status.result = False 40 return 41 42 # データ収集 43 game_info = GameInfo() 44 45 if not game_info.count: # 検索結果が0件のとき 46 m.set_headline(message.random_reply(m, "no_hits"), StyleOptions()) 47 m.status.result = False 48 return 49 50 df_results = g.params.read_data("RANKING_RESULTS").set_index("name") 51 df_ratings = aggregate.calculation_rating() 52 53 # 最終的なレーティング 54 final = df_ratings.ffill().tail(1).transpose() 55 final.columns = ["rate"] 56 final["name"] = final.index 57 58 df = pd.merge(df_results, final, on=["name"]).sort_values(by="rate", ascending=False) 59 df = df.query("count >= @g.params.stipulated") # 足切り 60 df["rank"] = 0 # 順位表示用カラム 61 62 # 集計対象外データの削除 63 if g.params.unregistered_replace: # 個人戦 64 for player in df.itertuples(): 65 if player.name not in g.cfg.member.lists: 66 df = df.drop(player.Index) 67 68 if not g.params.individual: # チーム戦 69 df = df.query("name != '未所属'") 70 71 # 順位偏差 / 得点偏差 72 df["point_dev"] = round((df["rpoint_avg"] - df["rpoint_avg"].mean()) / df["rpoint_avg"].std(ddof=0) * 10 + 50, 1) 73 df["rank_dev"] = round((df["rank_avg"] - df["rank_avg"].mean()) / df["rank_avg"].std(ddof=0) * -10 + 50, 1) 74 75 # 段位 76 if g.adapter.conf.badge_grade: 77 for idx in df.index: 78 name = str(df.at[idx, "name"]).replace(f"({g.cfg.setting.guest_mark})", "") 79 df.at[idx, "grade"] = badge.grade(name, False) 80 81 # 表示 82 if g.params.anonymous: 83 mapping_dict = formatter.anonymous_mapping(df["name"].unique().tolist()) 84 df["name"] = df["name"].replace(mapping_dict) 85 86 if df.empty: 87 m.set_headline(message.random_reply(m, "no_target"), StyleOptions()) 88 m.status.result = False 89 return 90 91 df["rank"] = df["rate"].rank(ascending=False, method="dense").astype("int") 92 df["rate"] = df["rate"].map(lambda v: round(v, 1)) 93 df = df.query("rank <= @g.params.ranked").filter( 94 items=["rank", "name", "rate", "rank_distr", "rank_avg", "rank_dev", "rpoint_avg", "point_dev", "grade"], 95 ) 96 97 # 非表示項目を削除 98 df = formatter.df_drop(df, list(g.cfg.rule.dropitems(g.params.rule_version))) 99 100 m.set_headline(message.header(game_info, m, add_text, 1), StyleOptions(title=title)) 101 options: StyleOptions = StyleOptions( 102 title=title, 103 data_kind=StyleOptions.DataKind.RATING, 104 rename_type=StyleOptions.RenameType.SHORT, 105 base_name="rating", 106 format_type="default", 107 summarize=False, 108 codeblock=True, 109 ) 110 111 data: "MessageType" 112 match g.params.format.lower(): 113 case "csv": 114 options.format_type = "csv" 115 data = converter.save_output(df, options, m.post.headline) 116 case "text" | "txt": 117 options.format_type = "txt" 118 data = converter.save_output(df, options, m.post.headline) 119 case _: 120 options.key_title = False 121 data = df 122 123 m.set_message(data, options)
レーティングを集計して返す
Arguments:
- m (MessageParserProtocol): メッセージデータ