libs.utils.converter

libs/utils/converter.py

  1"""
  2libs/utils/converter.py
  3"""
  4
  5import textwrap
  6from typing import TYPE_CHECKING, Optional, Union, cast
  7
  8import pandas as pd
  9from table2ascii import Alignment, PresetStyle, table2ascii
 10from tabulate import tabulate
 11
 12import libs.global_value as g
 13from libs.utils import formatter, textutil
 14
 15if TYPE_CHECKING:
 16    from pathlib import Path
 17
 18
 19def save_output(
 20    df: pd.DataFrame,
 21    kind: str,
 22    filename: str,
 23    headline: Optional[str] = None,
 24    suffix: Optional[str] = None,
 25) -> Union["Path", None]:
 26    """指定されたフォーマットでdfを保存する
 27
 28    Args:
 29        df (pd.DataFrame): 描写対象データ
 30        kind (str): フォーマット
 31        filename (str): 保存ファイル名
 32        headline (Optional[str], optional): 集計情報(ヘッダコメント). Defaults to None.
 33        suffix (Optional[str], optional): 保存ファイル名に追加する文字列. Defaults to None.
 34
 35    Returns:
 36        Path: 保存したファイルパス
 37        None: 未知のフォーマットが指定された場合
 38    """
 39
 40    match kind.lower():
 41        case "csv":
 42            data = df.to_csv(index=False)
 43        case "text" | "txt":
 44            data = df.to_markdown(
 45                index=False,
 46                tablefmt="outline",
 47                floatfmt=formatter.floatfmt_adjust(df),
 48                colalign=formatter.column_alignment(df, False),
 49                # headersalign=column_alignment(df, True),  # ToDo: python-tabulate >= 0.10.0
 50            )
 51        case _:
 52            return None
 53
 54    # 保存
 55    save_file = textutil.save_file_path(filename, True)
 56    if suffix and g.params.get("filename"):
 57        save_file = save_file.with_name(f"{save_file.stem}_{suffix}{save_file.suffix}")
 58
 59    with open(save_file, "w", encoding="utf-8") as writefile:
 60        if headline is not None:  # ヘッダ書き込み
 61            for line in headline.splitlines():
 62                writefile.writelines(f"# {line}\n")
 63            writefile.writelines("\n")
 64
 65        writefile.writelines(data)
 66
 67    return save_file
 68
 69
 70def df_to_text_table(df: pd.DataFrame, step: int = 40, index: bool = False) -> dict:
 71    """DataFrameからテキストテーブルの生成
 72
 73    Args:
 74        df (pd.DataFrame): 対象データ
 75        step (int, optional): 分割行. Defaults to 40.
 76        index (bool, optional): インデックスを含める. Defaults to False.
 77
 78    Returns:
 79        dict: 生成テーブル
 80    """
 81
 82    # ヘッダ/位置
 83    header: list = []
 84    alignments: list = []
 85    if index:
 86        df.reset_index(inplace=True, drop=True)
 87        df.index += 1
 88        header.append("")
 89    for col in df.columns:
 90        header.append(col)
 91        match col:
 92            case "名前" | "プレイヤー名":
 93                alignments.append(Alignment.LEFT)
 94            case "順位分布":
 95                alignments.append(Alignment.LEFT)
 96            case _:
 97                alignments.append(Alignment.RIGHT)
 98
 99    # 表データ
100    body: list = []
101    data: list = []
102    for row in df.to_dict(orient="records"):
103        data.clear()
104        for k, v in row.items():
105            match k:
106                case "通算" | "平均" | "平均素点":
107                    data.append(f" {v:+.1f}".replace(" -", "▲"))
108                case "平順" | "平均順位":
109                    data.append(f"{v:.2f}")
110                case "レート":
111                    data.append(f"{v:.1f}")
112                case "順位偏差" | "得点偏差":
113                    data.append(f"{v:.0f}")
114                case _:
115                    data.append(str(v).replace("nan", "*****"))
116            if index:
117                data.insert(0, "")
118        body.append(data.copy())
119
120    # 表生成/分割
121    my_style = PresetStyle.plain
122    my_style.heading_row_sep = "-"
123    my_style.heading_row_right_tee = ""
124    my_style.heading_row_left_tee = ""
125
126    table_data: dict = {}
127    for idx, table_body in enumerate(textutil.split_balanced(body, step)):
128        output = table2ascii(
129            header=header,
130            body=table_body,
131            style=my_style,
132            cell_padding=0,
133            first_col_heading=index,
134            alignments=alignments,
135        )
136        table_data.update({f"{idx}": output})
137
138    return table_data
139
140
141def df_to_results_details(df: pd.DataFrame) -> dict:
142    """戦績(詳細)データをテキスト変換
143
144    Args:
145        df (pd.DataFrame): 対象データ
146
147    Returns:
148        dict: 整形テキスト
149    """
150
151    data_list: list = []
152    for x in df.to_dict(orient="index").values():
153        work_df = pd.DataFrame({
154            "東家:": [v for k, v in cast(dict, x).items() if str(k).startswith("東家")],
155            "南家:": [v for k, v in cast(dict, x).items() if str(k).startswith("南家")],
156            "西家:": [v for k, v in cast(dict, x).items() if str(k).startswith("西家")],
157            "北家:": [v for k, v in cast(dict, x).items() if str(k).startswith("北家")],
158        }, index=["name", "rpoint", "rank", "point", "grandslam"]).T
159
160        work_df["rpoint"] = work_df.apply(lambda v: f"<>{v["rpoint"]:8d}点".replace("-", "▲"), axis=1)
161        work_df["point"] = work_df.apply(lambda v: f"(<>{v["point"]:7.1f}pt)".replace("-", "▲"), axis=1)
162        work_df["rank"] = work_df.apply(lambda v: f"{v["rank"]}位", axis=1)
163        data = work_df.to_markdown(tablefmt="tsv", headers=[], floatfmt=formatter.floatfmt_adjust(work_df)).replace("<>", "")
164
165        ret = f"{str(x["日時"]).replace("-", "/")} {x["備考"]}\n"
166        ret += textwrap.indent(data, "\t") + "\n"
167
168        data_list.append(ret)
169
170    return {str(idx): x for idx, x in enumerate(formatter.group_strings(data_list, 2000))}
171
172
173def df_to_results_simple(df: pd.DataFrame) -> dict:
174    """戦績(簡易)データをテキスト変換
175
176    Args:
177        df (pd.DataFrame): 対象データ
178
179    Returns:
180        dict: 整形テキスト
181    """
182
183    data_list: list = []
184    for x in df.to_dict(orient="index").values():
185        vs_guest = ""
186        if x["備考"] != "":
187            vs_guest = f"({g.cfg.setting.guest_mark}) "
188
189        ret = f"\t{vs_guest}{str(x["日時"]).replace("-", "/")}  "
190        ret += f"{x["座席"]}\t{x["順位"]}\t{x["素点"]:8d}\t({x["ポイント"]:7.1f}pt)\t{x["役満和了"]}".replace("-", "▲")
191        data_list.append(ret)
192
193    return {str(idx): x for idx, x in enumerate(formatter.group_strings(data_list, 2500))}
194
195
196def df_to_ranking(df: pd.DataFrame, title: str, step: int = 40) -> dict:
197    """DataFrameからランキングテーブルを生成
198
199    Args:
200        df (pd.DataFrame): 対象データ
201        title (str): 種別
202        step (int, optional): 分割行. Defaults to 40.
203
204    Returns:
205        dict: 整形テキスト
206    """
207
208    # 表示内容
209    body: list = []
210    alignments: list = []
211    match title:
212        case "ゲーム参加率":
213            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
214            for x in df.itertuples():
215                body.append([
216                    f"{x.順位}:",
217                    x.プレイヤー名,
218                    f"{x.ゲーム参加率:>7.2%}",
219                    f"({x.ゲーム数:4d}G / {x.集計ゲーム数:4d}G)",
220                ])
221        case "通算ポイント":
222            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
223            for x in df.itertuples():
224                body.append([
225                    f"{x.順位}:",
226                    x.プレイヤー名,
227                    f"{x.通算ポイント:>+8.1f}pt".replace("-", "▲"),
228                    f"({x.ゲーム数:4d}G)",
229                ])
230        case "平均ポイント":
231            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
232            for x in df.itertuples():
233                body.append([
234                    f"{x.順位}:",
235                    x.プレイヤー名,
236                    f"{x.平均ポイント:>+8.1f}pt".replace("-", "▲"),
237                    f"({x.通算ポイント:>+8.1f}pt / {x.ゲーム数:4d}G)".replace("-", "▲"),
238                ])
239        case "平均収支":
240            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
241            for x in df.itertuples():
242                body.append([
243                    f"{x.順位}:",
244                    x.プレイヤー名,
245                    f"{x.平均収支:>6.0f}点".replace("-", "▲"),
246                    f"({x.平均素点:>6.0f}点 / {x.ゲーム数:4d}G)".replace("-", "▲"),
247                ])
248        case "トップ率":
249            df = df.rename(columns={"1位率": "トップ率", "1位数": "トップ数"})
250            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
251            for x in df.itertuples():
252                body.append([
253                    f"{x.順位}:",
254                    x.プレイヤー名,
255                    f"{x.トップ率:>7.2%}",
256                    f"({x.トップ数:3d} / {x.ゲーム数:4d}G)",
257                ])
258        case "連対率":
259            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
260            for x in df.itertuples():
261                body.append([
262                    f"{x.順位}:",
263                    x.プレイヤー名,
264                    f"{x.連対率:>7.2%}",
265                    f"({x.連対数:3d} / {x.ゲーム数:4d}G)",
266                ])
267        case "ラス回避率":
268            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
269            for x in df.itertuples():
270                body.append([
271                    f"{x.順位}:",
272                    x.プレイヤー名,
273                    f"{x.ラス回避率:>7.2%}",
274                    f"({x.ラス回避数:3d} / {x.ゲーム数:4d}G)",
275                ])
276        case "トビ率":
277            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
278            for x in df.itertuples():
279                body.append([
280                    f"{x.順位}:",
281                    x.プレイヤー名,
282                    f"{x.トビ率:>7.2%}",
283                    f"({x.トビ数:3d} / {x.ゲーム数:4d}G)",
284                ])
285        case "平均順位":
286            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
287            for x in df.itertuples():
288                body.append([
289                    f"{x.順位}:",
290                    x.プレイヤー名,
291                    f"{x.平均順位:>4.2f}",
292                    f"({x.順位分布})",
293                ])
294        case "役満和了率":
295            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
296            for x in df.itertuples():
297                body.append([
298                    f"{x.順位}:",
299                    x.プレイヤー名,
300                    f"{x.役満和了率:>7.2%}",
301                    f"({x.役満和了数:3d} / {x.ゲーム数:4d}G)",
302                ])
303        case "最大素点":
304            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
305            for x in df.itertuples():
306                body.append([
307                    f"{x.順位}:",
308                    x.プレイヤー名,
309                    f"{x.最大素点:>6.0f}点".replace("-", "▲"),
310                    f"({x.最大獲得ポイント:>+8.1f}pt)".replace("-", "▲"),
311                ])
312        case "連続トップ":
313            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
314            for x in df.itertuples():
315                body.append([
316                    f"{x.順位}:",
317                    x.プレイヤー名,
318                    f"{x.連続トップ:>2d}連続",
319                    f"({x.ゲーム数:4d}G)",
320                ])
321        case "連続連対":
322            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
323            for x in df.itertuples():
324                body.append([
325                    f"{x.順位}:",
326                    x.プレイヤー名,
327                    f"{x.連続連対:>2d}連続",
328                    f"({x.ゲーム数:4d}G)",
329                ])
330        case "連続ラス回避":
331            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
332            for x in df.itertuples():
333                body.append([
334                    f"{x.順位}:",
335                    x.プレイヤー名,
336                    f"{x.連続ラス回避:>2d}連続",
337                    f"({x.ゲーム数:4d}G)",
338                ])
339        case _:
340            return {}
341
342    # 整形/分割
343    ret: dict = {}
344    if step:
345        data = textutil.split_balanced(body, step)
346        last_block = len(data)
347    else:
348        last_block = 1
349
350    if last_block == 1:
351        output = table2ascii(
352            body=body,
353            style=PresetStyle.plain,
354            cell_padding=0,
355            first_col_heading=True,
356            alignments=alignments,
357        )
358        ret.update({title: output})
359    else:
360        count = 0
361        for x in data:
362            count += 1
363            output = table2ascii(
364                body=x,
365                style=PresetStyle.plain,
366                cell_padding=0,
367                first_col_heading=True,
368                alignments=alignments,
369            )
370            ret.update({f"{title} ({count}/{last_block})": output})
371
372    return ret
373
374
375def df_to_remarks(df: pd.DataFrame) -> dict:
376    """DataFrameからメモテーブルを生成
377
378    Args:
379        df (pd.DataFrame): 対象データ
380
381    Returns:
382        dict: 整形テキスト
383    """
384
385    for col in df.columns:
386        match col:
387            case "日時":
388                df["日時"] = df["日時"].map(lambda x: str(x).replace("-", "/"))
389            case "卓外":
390                df["卓外"] = df["卓外"].map(lambda x: f"{x}pt".replace("-", "▲"))
391
392    if "卓外" in df.columns:
393        df["表示"] = df.apply(lambda x: f"{x["日時"]} {x["内容"]} {x["卓外"]} ({x["名前"]})", axis=1)
394    elif "和了役" in df.columns:
395        df["表示"] = df.apply(lambda x: f"{x["日時"]} {x["和了役"]} ({x["名前"]})", axis=1)
396    else:
397        df["表示"] = df.apply(lambda x: f"{x["日時"]} {x["内容"]} ({x["名前"]})", axis=1)
398
399    tbl = tabulate(df.filter(items=["表示"]).values, showindex=False).splitlines()[1:-1]
400
401    return {"0": "\n".join(tbl)}
402
403
404def df_to_count(df: pd.DataFrame, title: str, indent: int = 0) -> dict:
405    """DataFrameからメモの回数表示を生成
406
407    Args:
408        df (pd.DataFrame): 対象データ
409        title (str): _description_
410        indent (int, optional): インデント. Defaults to 0.
411
412    Returns:
413        dict: 整形テキスト
414    """
415    match title:
416        case "役満和了":
417            df["表示"] = df.apply(lambda x: f"{x["和了役"]}{x["回数"]} 回", axis=1)
418        case "卓外ポイント":
419            df["表示"] = df.apply(lambda x: f"{x["内容"]}{x["回数"]} 回 ({x["ポイント合計"]:.1f}pt)".replace("-", "▲"), axis=1)
420        case "その他":
421            df["表示"] = df.apply(lambda x: f"{x["内容"]}{x["回数"]} 回", axis=1)
422
423    tbl = tabulate(df.filter(items=["表示"]).values, showindex=False).splitlines()[1:-1]
424    return {"0": textwrap.indent("\n".join(tbl), "\t" * indent)}
425
426
427def df_to_seat_data(df: pd.DataFrame, indent: int = 0) -> dict:
428    """座席データ生成
429
430    Args:
431        df (pd.DataFrame): 対象データ
432        indent (int, optional): インデント. Defaults to 0.
433
434    Returns:
435        dict: 整形テキスト
436    """
437
438    # 表示加工
439    df["順位分布(平均順位)"] = df.apply(lambda x: f"{x["順位分布"]} ({x["平均順位"]:.2f})", axis=1)
440    df.drop(columns=["順位分布", "平均順位"], inplace=True)
441    df["席"] = df.apply(lambda x: f"{x["席"]}:", axis=1)
442    if "トビ" in df.columns:
443        df["トビ"] = df.apply(lambda x: f"/ {x["トビ"]:3d}", axis=1)
444    if "役満和了" in df.columns:
445        df["役満和了"] = df.apply(lambda x: f"/ {x["役満和了"]:3d}", axis=1)
446
447    #
448    df = df.filter(items=["席", "順位分布(平均順位)", "トビ", "役満和了"]).rename(
449        columns={
450            "席": "# 席:",
451            "トビ": "/ トビ",
452            "役満和了": "/ 役満 #"
453        }
454    )
455
456    tbl = df.to_markdown(tablefmt="tsv", index=False).replace("0.00", "-.--").replace(" \t", "")
457    return {"0": textwrap.indent(tbl, "\t" * indent)}
def save_output( df: pandas.core.frame.DataFrame, kind: str, filename: str, headline: Optional[str] = None, suffix: Optional[str] = None) -> Optional[pathlib.Path]:
20def save_output(
21    df: pd.DataFrame,
22    kind: str,
23    filename: str,
24    headline: Optional[str] = None,
25    suffix: Optional[str] = None,
26) -> Union["Path", None]:
27    """指定されたフォーマットでdfを保存する
28
29    Args:
30        df (pd.DataFrame): 描写対象データ
31        kind (str): フォーマット
32        filename (str): 保存ファイル名
33        headline (Optional[str], optional): 集計情報(ヘッダコメント). Defaults to None.
34        suffix (Optional[str], optional): 保存ファイル名に追加する文字列. Defaults to None.
35
36    Returns:
37        Path: 保存したファイルパス
38        None: 未知のフォーマットが指定された場合
39    """
40
41    match kind.lower():
42        case "csv":
43            data = df.to_csv(index=False)
44        case "text" | "txt":
45            data = df.to_markdown(
46                index=False,
47                tablefmt="outline",
48                floatfmt=formatter.floatfmt_adjust(df),
49                colalign=formatter.column_alignment(df, False),
50                # headersalign=column_alignment(df, True),  # ToDo: python-tabulate >= 0.10.0
51            )
52        case _:
53            return None
54
55    # 保存
56    save_file = textutil.save_file_path(filename, True)
57    if suffix and g.params.get("filename"):
58        save_file = save_file.with_name(f"{save_file.stem}_{suffix}{save_file.suffix}")
59
60    with open(save_file, "w", encoding="utf-8") as writefile:
61        if headline is not None:  # ヘッダ書き込み
62            for line in headline.splitlines():
63                writefile.writelines(f"# {line}\n")
64            writefile.writelines("\n")
65
66        writefile.writelines(data)
67
68    return save_file

指定されたフォーマットでdfを保存する

Arguments:
  • df (pd.DataFrame): 描写対象データ
  • kind (str): フォーマット
  • filename (str): 保存ファイル名
  • headline (Optional[str], optional): 集計情報(ヘッダコメント). Defaults to None.
  • suffix (Optional[str], optional): 保存ファイル名に追加する文字列. Defaults to None.
Returns:

Path: 保存したファイルパス None: 未知のフォーマットが指定された場合

def df_to_text_table( df: pandas.core.frame.DataFrame, step: int = 40, index: bool = False) -> dict:
 71def df_to_text_table(df: pd.DataFrame, step: int = 40, index: bool = False) -> dict:
 72    """DataFrameからテキストテーブルの生成
 73
 74    Args:
 75        df (pd.DataFrame): 対象データ
 76        step (int, optional): 分割行. Defaults to 40.
 77        index (bool, optional): インデックスを含める. Defaults to False.
 78
 79    Returns:
 80        dict: 生成テーブル
 81    """
 82
 83    # ヘッダ/位置
 84    header: list = []
 85    alignments: list = []
 86    if index:
 87        df.reset_index(inplace=True, drop=True)
 88        df.index += 1
 89        header.append("")
 90    for col in df.columns:
 91        header.append(col)
 92        match col:
 93            case "名前" | "プレイヤー名":
 94                alignments.append(Alignment.LEFT)
 95            case "順位分布":
 96                alignments.append(Alignment.LEFT)
 97            case _:
 98                alignments.append(Alignment.RIGHT)
 99
100    # 表データ
101    body: list = []
102    data: list = []
103    for row in df.to_dict(orient="records"):
104        data.clear()
105        for k, v in row.items():
106            match k:
107                case "通算" | "平均" | "平均素点":
108                    data.append(f" {v:+.1f}".replace(" -", "▲"))
109                case "平順" | "平均順位":
110                    data.append(f"{v:.2f}")
111                case "レート":
112                    data.append(f"{v:.1f}")
113                case "順位偏差" | "得点偏差":
114                    data.append(f"{v:.0f}")
115                case _:
116                    data.append(str(v).replace("nan", "*****"))
117            if index:
118                data.insert(0, "")
119        body.append(data.copy())
120
121    # 表生成/分割
122    my_style = PresetStyle.plain
123    my_style.heading_row_sep = "-"
124    my_style.heading_row_right_tee = ""
125    my_style.heading_row_left_tee = ""
126
127    table_data: dict = {}
128    for idx, table_body in enumerate(textutil.split_balanced(body, step)):
129        output = table2ascii(
130            header=header,
131            body=table_body,
132            style=my_style,
133            cell_padding=0,
134            first_col_heading=index,
135            alignments=alignments,
136        )
137        table_data.update({f"{idx}": output})
138
139    return table_data

DataFrameからテキストテーブルの生成

Arguments:
  • df (pd.DataFrame): 対象データ
  • step (int, optional): 分割行. Defaults to 40.
  • index (bool, optional): インデックスを含める. Defaults to False.
Returns:

dict: 生成テーブル

def df_to_results_details(df: pandas.core.frame.DataFrame) -> dict:
142def df_to_results_details(df: pd.DataFrame) -> dict:
143    """戦績(詳細)データをテキスト変換
144
145    Args:
146        df (pd.DataFrame): 対象データ
147
148    Returns:
149        dict: 整形テキスト
150    """
151
152    data_list: list = []
153    for x in df.to_dict(orient="index").values():
154        work_df = pd.DataFrame({
155            "東家:": [v for k, v in cast(dict, x).items() if str(k).startswith("東家")],
156            "南家:": [v for k, v in cast(dict, x).items() if str(k).startswith("南家")],
157            "西家:": [v for k, v in cast(dict, x).items() if str(k).startswith("西家")],
158            "北家:": [v for k, v in cast(dict, x).items() if str(k).startswith("北家")],
159        }, index=["name", "rpoint", "rank", "point", "grandslam"]).T
160
161        work_df["rpoint"] = work_df.apply(lambda v: f"<>{v["rpoint"]:8d}点".replace("-", "▲"), axis=1)
162        work_df["point"] = work_df.apply(lambda v: f"(<>{v["point"]:7.1f}pt)".replace("-", "▲"), axis=1)
163        work_df["rank"] = work_df.apply(lambda v: f"{v["rank"]}位", axis=1)
164        data = work_df.to_markdown(tablefmt="tsv", headers=[], floatfmt=formatter.floatfmt_adjust(work_df)).replace("<>", "")
165
166        ret = f"{str(x["日時"]).replace("-", "/")} {x["備考"]}\n"
167        ret += textwrap.indent(data, "\t") + "\n"
168
169        data_list.append(ret)
170
171    return {str(idx): x for idx, x in enumerate(formatter.group_strings(data_list, 2000))}

戦績(詳細)データをテキスト変換

Arguments:
  • df (pd.DataFrame): 対象データ
Returns:

dict: 整形テキスト

def df_to_results_simple(df: pandas.core.frame.DataFrame) -> dict:
174def df_to_results_simple(df: pd.DataFrame) -> dict:
175    """戦績(簡易)データをテキスト変換
176
177    Args:
178        df (pd.DataFrame): 対象データ
179
180    Returns:
181        dict: 整形テキスト
182    """
183
184    data_list: list = []
185    for x in df.to_dict(orient="index").values():
186        vs_guest = ""
187        if x["備考"] != "":
188            vs_guest = f"({g.cfg.setting.guest_mark}) "
189
190        ret = f"\t{vs_guest}{str(x["日時"]).replace("-", "/")}  "
191        ret += f"{x["座席"]}\t{x["順位"]}\t{x["素点"]:8d}\t({x["ポイント"]:7.1f}pt)\t{x["役満和了"]}".replace("-", "▲")
192        data_list.append(ret)
193
194    return {str(idx): x for idx, x in enumerate(formatter.group_strings(data_list, 2500))}

戦績(簡易)データをテキスト変換

Arguments:
  • df (pd.DataFrame): 対象データ
Returns:

dict: 整形テキスト

def df_to_ranking(df: pandas.core.frame.DataFrame, title: str, step: int = 40) -> dict:
197def df_to_ranking(df: pd.DataFrame, title: str, step: int = 40) -> dict:
198    """DataFrameからランキングテーブルを生成
199
200    Args:
201        df (pd.DataFrame): 対象データ
202        title (str): 種別
203        step (int, optional): 分割行. Defaults to 40.
204
205    Returns:
206        dict: 整形テキスト
207    """
208
209    # 表示内容
210    body: list = []
211    alignments: list = []
212    match title:
213        case "ゲーム参加率":
214            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
215            for x in df.itertuples():
216                body.append([
217                    f"{x.順位}:",
218                    x.プレイヤー名,
219                    f"{x.ゲーム参加率:>7.2%}",
220                    f"({x.ゲーム数:4d}G / {x.集計ゲーム数:4d}G)",
221                ])
222        case "通算ポイント":
223            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
224            for x in df.itertuples():
225                body.append([
226                    f"{x.順位}:",
227                    x.プレイヤー名,
228                    f"{x.通算ポイント:>+8.1f}pt".replace("-", "▲"),
229                    f"({x.ゲーム数:4d}G)",
230                ])
231        case "平均ポイント":
232            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
233            for x in df.itertuples():
234                body.append([
235                    f"{x.順位}:",
236                    x.プレイヤー名,
237                    f"{x.平均ポイント:>+8.1f}pt".replace("-", "▲"),
238                    f"({x.通算ポイント:>+8.1f}pt / {x.ゲーム数:4d}G)".replace("-", "▲"),
239                ])
240        case "平均収支":
241            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
242            for x in df.itertuples():
243                body.append([
244                    f"{x.順位}:",
245                    x.プレイヤー名,
246                    f"{x.平均収支:>6.0f}点".replace("-", "▲"),
247                    f"({x.平均素点:>6.0f}点 / {x.ゲーム数:4d}G)".replace("-", "▲"),
248                ])
249        case "トップ率":
250            df = df.rename(columns={"1位率": "トップ率", "1位数": "トップ数"})
251            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
252            for x in df.itertuples():
253                body.append([
254                    f"{x.順位}:",
255                    x.プレイヤー名,
256                    f"{x.トップ率:>7.2%}",
257                    f"({x.トップ数:3d} / {x.ゲーム数:4d}G)",
258                ])
259        case "連対率":
260            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
261            for x in df.itertuples():
262                body.append([
263                    f"{x.順位}:",
264                    x.プレイヤー名,
265                    f"{x.連対率:>7.2%}",
266                    f"({x.連対数:3d} / {x.ゲーム数:4d}G)",
267                ])
268        case "ラス回避率":
269            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
270            for x in df.itertuples():
271                body.append([
272                    f"{x.順位}:",
273                    x.プレイヤー名,
274                    f"{x.ラス回避率:>7.2%}",
275                    f"({x.ラス回避数:3d} / {x.ゲーム数:4d}G)",
276                ])
277        case "トビ率":
278            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
279            for x in df.itertuples():
280                body.append([
281                    f"{x.順位}:",
282                    x.プレイヤー名,
283                    f"{x.トビ率:>7.2%}",
284                    f"({x.トビ数:3d} / {x.ゲーム数:4d}G)",
285                ])
286        case "平均順位":
287            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
288            for x in df.itertuples():
289                body.append([
290                    f"{x.順位}:",
291                    x.プレイヤー名,
292                    f"{x.平均順位:>4.2f}",
293                    f"({x.順位分布})",
294                ])
295        case "役満和了率":
296            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
297            for x in df.itertuples():
298                body.append([
299                    f"{x.順位}:",
300                    x.プレイヤー名,
301                    f"{x.役満和了率:>7.2%}",
302                    f"({x.役満和了数:3d} / {x.ゲーム数:4d}G)",
303                ])
304        case "最大素点":
305            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
306            for x in df.itertuples():
307                body.append([
308                    f"{x.順位}:",
309                    x.プレイヤー名,
310                    f"{x.最大素点:>6.0f}点".replace("-", "▲"),
311                    f"({x.最大獲得ポイント:>+8.1f}pt)".replace("-", "▲"),
312                ])
313        case "連続トップ":
314            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
315            for x in df.itertuples():
316                body.append([
317                    f"{x.順位}:",
318                    x.プレイヤー名,
319                    f"{x.連続トップ:>2d}連続",
320                    f"({x.ゲーム数:4d}G)",
321                ])
322        case "連続連対":
323            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
324            for x in df.itertuples():
325                body.append([
326                    f"{x.順位}:",
327                    x.プレイヤー名,
328                    f"{x.連続連対:>2d}連続",
329                    f"({x.ゲーム数:4d}G)",
330                ])
331        case "連続ラス回避":
332            alignments = [Alignment.RIGHT, Alignment.LEFT, Alignment.RIGHT, Alignment.LEFT]
333            for x in df.itertuples():
334                body.append([
335                    f"{x.順位}:",
336                    x.プレイヤー名,
337                    f"{x.連続ラス回避:>2d}連続",
338                    f"({x.ゲーム数:4d}G)",
339                ])
340        case _:
341            return {}
342
343    # 整形/分割
344    ret: dict = {}
345    if step:
346        data = textutil.split_balanced(body, step)
347        last_block = len(data)
348    else:
349        last_block = 1
350
351    if last_block == 1:
352        output = table2ascii(
353            body=body,
354            style=PresetStyle.plain,
355            cell_padding=0,
356            first_col_heading=True,
357            alignments=alignments,
358        )
359        ret.update({title: output})
360    else:
361        count = 0
362        for x in data:
363            count += 1
364            output = table2ascii(
365                body=x,
366                style=PresetStyle.plain,
367                cell_padding=0,
368                first_col_heading=True,
369                alignments=alignments,
370            )
371            ret.update({f"{title} ({count}/{last_block})": output})
372
373    return ret

DataFrameからランキングテーブルを生成

Arguments:
  • df (pd.DataFrame): 対象データ
  • title (str): 種別
  • step (int, optional): 分割行. Defaults to 40.
Returns:

dict: 整形テキスト

def df_to_remarks(df: pandas.core.frame.DataFrame) -> dict:
376def df_to_remarks(df: pd.DataFrame) -> dict:
377    """DataFrameからメモテーブルを生成
378
379    Args:
380        df (pd.DataFrame): 対象データ
381
382    Returns:
383        dict: 整形テキスト
384    """
385
386    for col in df.columns:
387        match col:
388            case "日時":
389                df["日時"] = df["日時"].map(lambda x: str(x).replace("-", "/"))
390            case "卓外":
391                df["卓外"] = df["卓外"].map(lambda x: f"{x}pt".replace("-", "▲"))
392
393    if "卓外" in df.columns:
394        df["表示"] = df.apply(lambda x: f"{x["日時"]} {x["内容"]} {x["卓外"]} ({x["名前"]})", axis=1)
395    elif "和了役" in df.columns:
396        df["表示"] = df.apply(lambda x: f"{x["日時"]} {x["和了役"]} ({x["名前"]})", axis=1)
397    else:
398        df["表示"] = df.apply(lambda x: f"{x["日時"]} {x["内容"]} ({x["名前"]})", axis=1)
399
400    tbl = tabulate(df.filter(items=["表示"]).values, showindex=False).splitlines()[1:-1]
401
402    return {"0": "\n".join(tbl)}

DataFrameからメモテーブルを生成

Arguments:
  • df (pd.DataFrame): 対象データ
Returns:

dict: 整形テキスト

def df_to_count(df: pandas.core.frame.DataFrame, title: str, indent: int = 0) -> dict:
405def df_to_count(df: pd.DataFrame, title: str, indent: int = 0) -> dict:
406    """DataFrameからメモの回数表示を生成
407
408    Args:
409        df (pd.DataFrame): 対象データ
410        title (str): _description_
411        indent (int, optional): インデント. Defaults to 0.
412
413    Returns:
414        dict: 整形テキスト
415    """
416    match title:
417        case "役満和了":
418            df["表示"] = df.apply(lambda x: f"{x["和了役"]}{x["回数"]} 回", axis=1)
419        case "卓外ポイント":
420            df["表示"] = df.apply(lambda x: f"{x["内容"]}{x["回数"]} 回 ({x["ポイント合計"]:.1f}pt)".replace("-", "▲"), axis=1)
421        case "その他":
422            df["表示"] = df.apply(lambda x: f"{x["内容"]}{x["回数"]} 回", axis=1)
423
424    tbl = tabulate(df.filter(items=["表示"]).values, showindex=False).splitlines()[1:-1]
425    return {"0": textwrap.indent("\n".join(tbl), "\t" * indent)}

DataFrameからメモの回数表示を生成

Arguments:
  • df (pd.DataFrame): 対象データ
  • title (str): _description_
  • indent (int, optional): インデント. Defaults to 0.
Returns:

dict: 整形テキスト

def df_to_seat_data(df: pandas.core.frame.DataFrame, indent: int = 0) -> dict:
428def df_to_seat_data(df: pd.DataFrame, indent: int = 0) -> dict:
429    """座席データ生成
430
431    Args:
432        df (pd.DataFrame): 対象データ
433        indent (int, optional): インデント. Defaults to 0.
434
435    Returns:
436        dict: 整形テキスト
437    """
438
439    # 表示加工
440    df["順位分布(平均順位)"] = df.apply(lambda x: f"{x["順位分布"]} ({x["平均順位"]:.2f})", axis=1)
441    df.drop(columns=["順位分布", "平均順位"], inplace=True)
442    df["席"] = df.apply(lambda x: f"{x["席"]}:", axis=1)
443    if "トビ" in df.columns:
444        df["トビ"] = df.apply(lambda x: f"/ {x["トビ"]:3d}", axis=1)
445    if "役満和了" in df.columns:
446        df["役満和了"] = df.apply(lambda x: f"/ {x["役満和了"]:3d}", axis=1)
447
448    #
449    df = df.filter(items=["席", "順位分布(平均順位)", "トビ", "役満和了"]).rename(
450        columns={
451            "席": "# 席:",
452            "トビ": "/ トビ",
453            "役満和了": "/ 役満 #"
454        }
455    )
456
457    tbl = df.to_markdown(tablefmt="tsv", index=False).replace("0.00", "-.--").replace(" \t", "")
458    return {"0": textwrap.indent(tbl, "\t" * indent)}

座席データ生成

Arguments:
  • df (pd.DataFrame): 対象データ
  • indent (int, optional): インデント. Defaults to 0.
Returns:

dict: 整形テキスト