spells-mtg 0.5.1__tar.gz → 0.5.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of spells-mtg might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spells-mtg
3
- Version: 0.5.1
3
+ Version: 0.5.3
4
4
  Summary: analaysis of 17Lands.com public datasets
5
5
  Author-Email: Joel Barnes <oelarnes@gmail.com>
6
6
  License: MIT
@@ -294,7 +294,8 @@ summon(
294
294
  columns: list[str] | None = None,
295
295
  group_by: list[str] | None = None,
296
296
  filter_spec: dict | None = None,
297
- extensions: list[str] | None = None,
297
+ extensions: dict[str, ColSpec] | None = None,
298
+ card_context: pl.DataFrame | dict[str, dict[str, Any] | None = None,
298
299
  read_cache: bool = True,
299
300
  write_cache: bool = True,
300
301
  ) -> polars.DataFrame
@@ -283,7 +283,8 @@ summon(
283
283
  columns: list[str] | None = None,
284
284
  group_by: list[str] | None = None,
285
285
  filter_spec: dict | None = None,
286
- extensions: list[str] | None = None,
286
+ extensions: dict[str, ColSpec] | None = None,
287
+ card_context: pl.DataFrame | dict[str, dict[str, Any] | None = None,
287
288
  read_cache: bool = True,
288
289
  write_cache: bool = True,
289
290
  ) -> polars.DataFrame
@@ -11,7 +11,7 @@ dependencies = [
11
11
  ]
12
12
  requires-python = ">=3.11"
13
13
  readme = "README.md"
14
- version = "0.5.1"
14
+ version = "0.5.3"
15
15
 
16
16
  [project.license]
17
17
  text = "MIT"
@@ -5,7 +5,6 @@ import polars as pl
5
5
 
6
6
  from spells.enums import View, ColName, ColType
7
7
 
8
-
9
8
  @dataclass(frozen=True)
10
9
  class ColSpec:
11
10
  col_type: ColType
@@ -40,7 +39,7 @@ default_columns = [
40
39
  ColName.GIH_WR,
41
40
  ]
42
41
 
43
- specs: dict[str, ColSpec] = {
42
+ _specs: dict[str, ColSpec] = {
44
43
  ColName.NAME: ColSpec(
45
44
  col_type=ColType.GROUP_BY,
46
45
  views=[View.CARD],
@@ -477,6 +476,10 @@ specs: dict[str, ColSpec] = {
477
476
  col_type=ColType.AGG,
478
477
  expr=pl.col(ColName.DECK) + pl.col(ColName.SIDEBOARD),
479
478
  ),
479
+ ColName.NUM_IN_POOL_TOTAL: ColSpec(
480
+ col_type=ColType.AGG,
481
+ expr=pl.col(ColName.NUM_IN_POOL).sum(),
482
+ ),
480
483
  ColName.IN_POOL_WR: ColSpec(
481
484
  col_type=ColType.AGG,
482
485
  expr=(pl.col(ColName.WON_DECK) + pl.col(ColName.WON_SIDEBOARD))
@@ -555,4 +558,13 @@ specs: dict[str, ColSpec] = {
555
558
  }
556
559
 
557
560
  for item in ColName:
558
- assert item in specs, f"column {item} enumerated but not specified"
561
+ assert item in _specs, f"column {item} enumerated but not specified"
562
+
563
+ class GetSpecs:
564
+ def __init__(self, spec_dict: dict[str, ColSpec]):
565
+ self._specs = spec_dict
566
+ def __call__(self):
567
+ return dict(self._specs)
568
+
569
+ get_specs = GetSpecs(_specs)
570
+
@@ -6,7 +6,6 @@ Aggregate dataframes containing raw counts are cached in the local file system
6
6
  for performance.
7
7
  """
8
8
 
9
- import datetime
10
9
  import functools
11
10
  import hashlib
12
11
  import re
@@ -20,7 +19,7 @@ from spells.external import data_file_path
20
19
  import spells.cache
21
20
  import spells.filter
22
21
  import spells.manifest
23
- from spells.columns import ColDef, ColSpec
22
+ from spells.columns import ColDef, ColSpec, get_specs
24
23
  from spells.enums import View, ColName, ColType
25
24
 
26
25
 
@@ -356,16 +355,19 @@ def summon(
356
355
  columns: list[str] | None = None,
357
356
  group_by: list[str] | None = None,
358
357
  filter_spec: dict | None = None,
359
- extensions: dict[str, ColSpec] | None = None,
358
+ extensions: dict[str, ColSpec] | list[dict[str, ColSpec]] | None = None,
360
359
  use_streaming: bool = False,
361
360
  read_cache: bool = True,
362
361
  write_cache: bool = True,
363
362
  card_context: pl.DataFrame | dict[str, dict] | None = None
364
363
  ) -> pl.DataFrame:
365
- specs = dict(spells.columns.specs)
364
+ specs = get_specs()
366
365
 
367
366
  if extensions is not None:
368
- specs.update(extensions)
367
+ if not isinstance(extensions, list):
368
+ extensions = [extensions]
369
+ for ext in extensions:
370
+ specs.update(ext)
369
371
 
370
372
  col_def_map = _hydrate_col_defs(set_code, specs, card_context)
371
373
  m = spells.manifest.create(col_def_map, columns, group_by, filter_spec)
@@ -143,6 +143,7 @@ class ColName(StrEnum):
143
143
  GNS_WR = "gns_wr"
144
144
  IWD = "iwd"
145
145
  NUM_IN_POOL = "num_in_pool"
146
+ NUM_IN_POOL_TOTAL = "num_in_pool_total"
146
147
  IN_POOL_WR = "in_pool_wr"
147
148
  DECK_TOTAL = "deck_total"
148
149
  WON_DECK_TOTAL = "won_deck_total"
@@ -0,0 +1,182 @@
1
+ import math
2
+
3
+ import polars as pl
4
+
5
+ from spells.enums import ColType, ColName
6
+ from spells.columns import ColSpec
7
+ from spells.cache import spells_print
8
+
9
+ def print_ext(ext: dict[str, ColSpec]) -> None:
10
+ spells_print("create", "Created extensions:")
11
+ for key in ext:
12
+ print("\t" + key)
13
+
14
+
15
+ def attr_cols(attr, silent=False) -> dict[str, ColSpec]:
16
+ ext = {
17
+ f"seen_{attr}": ColSpec(
18
+ col_type=ColType.NAME_SUM,
19
+ expr=(lambda name, card_context: pl.lit(None) if card_context[name][attr] is None or math.isnan(card_context[name][attr]) else pl.when(pl.col(f"pack_card_{name}") > 0)
20
+ .then(card_context[name][attr])
21
+ .otherwise(None)),
22
+ ),
23
+ f"pick_{attr}": ColSpec(
24
+ col_type=ColType.PICK_SUM,
25
+ expr=lambda name, card_context: pl.lit(None) if card_context[name][attr] is None or math.isnan(card_context[name][attr]) else card_context[name][attr]
26
+ ),
27
+ f"seen_{attr}_greater": ColSpec(
28
+ col_type=ColType.NAME_SUM,
29
+ expr=lambda name: pl.col(f"seen_{attr}_{name}") > pl.col(f"pick_{attr}"),
30
+ ),
31
+ f"seen_{attr}_less": ColSpec(
32
+ col_type=ColType.NAME_SUM,
33
+ expr=lambda name: pl.col(f"seen_{attr}_{name}") < pl.col(f"pick_{attr}"),
34
+ ),
35
+ f"greatest_{attr}_seen": ColSpec(
36
+ col_type=ColType.PICK_SUM,
37
+ expr=lambda names: pl.max_horizontal([pl.col(f"seen_{attr}_{name}") for name in names]),
38
+ ),
39
+ f"least_{attr}_seen": ColSpec(
40
+ col_type=ColType.PICK_SUM,
41
+ expr=lambda names: pl.min_horizontal([pl.col(f"seen_{attr}_{name}") for name in names]),
42
+ ),
43
+ f"pick_{attr}_rank_greatest": ColSpec(
44
+ col_type=ColType.GROUP_BY,
45
+ expr=lambda names: pl.sum_horizontal([pl.col(f"seen_{attr}_greater_{name}") for name in names]) + 1,
46
+ ),
47
+ f"pick_{attr}_rank_least": ColSpec(
48
+ col_type=ColType.GROUP_BY,
49
+ expr=lambda names: pl.sum_horizontal([pl.col(f"seen_{attr}_less_{name}") for name in names]) + 1,
50
+ ),
51
+ f"pick_{attr}_rank_greatest_sum": ColSpec(
52
+ col_type=ColType.PICK_SUM,
53
+ expr=pl.col(f"pick_{attr}_rank_greatest")
54
+ ),
55
+ f"pick_{attr}_rank_least_sum": ColSpec(
56
+ col_type=ColType.PICK_SUM,
57
+ expr=pl.col(f"pick_{attr}_rank_least")
58
+ ),
59
+ f"pick_{attr}_vs_least": ColSpec(
60
+ col_type=ColType.PICK_SUM,
61
+ expr=pl.col(f"pick_{attr}") - pl.col(f"least_{attr}_seen"),
62
+ ),
63
+ f"pick_{attr}_vs_greatest": ColSpec(
64
+ col_type=ColType.PICK_SUM,
65
+ expr=pl.col(f"pick_{attr}") - pl.col(f"greatest_{attr}_seen"),
66
+ ),
67
+ f"pick_{attr}_vs_least_mean": ColSpec(
68
+ col_type=ColType.AGG,
69
+ expr=pl.col(f"pick_{attr}_vs_least") / pl.col(ColName.NUM_TAKEN),
70
+ ),
71
+ f"pick_{attr}_vs_greatest_mean": ColSpec(
72
+ col_type=ColType.AGG,
73
+ expr=pl.col(f"pick_{attr}_vs_greatest") / pl.col(ColName.NUM_TAKEN),
74
+ ),
75
+ f"least_{attr}_taken": ColSpec(
76
+ col_type=ColType.PICK_SUM,
77
+ expr=pl.col(f'pick_{attr}') <= pl.col(f'least_{attr}_seen'),
78
+ ),
79
+ f"least_{attr}_taken_rate": ColSpec(
80
+ col_type=ColType.AGG,
81
+ expr=pl.col(f"least_{attr}_taken") / pl.col(ColName.NUM_TAKEN),
82
+ ),
83
+ f"greatest_{attr}_taken": ColSpec(
84
+ col_type=ColType.PICK_SUM,
85
+ expr=pl.col(f'pick_{attr}') >= pl.col(f"greatest_{attr}_seen")
86
+ ),
87
+ f"greatest_{attr}_taken_rate": ColSpec(
88
+ col_type=ColType.AGG,
89
+ expr=pl.col(f"greatest_{attr}_taken") / pl.col(ColName.NUM_TAKEN),
90
+ ),
91
+ f"pick_{attr}_mean": ColSpec(
92
+ col_type=ColType.AGG,
93
+ expr=pl.col(f"pick_{attr}") / pl.col(ColName.NUM_TAKEN)
94
+ ),
95
+ f"{attr}_deck_weight_group": ColSpec(
96
+ col_type=ColType.AGG,
97
+ expr=pl.col(f"{attr}") * pl.col(ColName.DECK)
98
+ ),
99
+ f"{attr}_deck_weight_total": ColSpec(
100
+ col_type=ColType.AGG,
101
+ expr=pl.col(f"{attr}_deck_weight_group").sum()
102
+ ),
103
+ f"{attr}_dw_mean": ColSpec(
104
+ col_type=ColType.AGG,
105
+ expr=pl.col(f"{attr}_deck_weight_total") / pl.col(ColName.DECK_TOTAL),
106
+ ),
107
+ f"{attr}_dw_excess": ColSpec(
108
+ col_type=ColType.AGG,
109
+ expr=pl.col(f"{attr}_dw_mean") - pl.col(f"{attr}"),
110
+ ),
111
+ f"{attr}_dw_var": ColSpec(
112
+ col_type=ColType.AGG,
113
+ expr=(pl.col(f"{attr}_dw_excess").pow(2) * pl.col(ColName.DECK)) / pl.col(ColName.DECK_TOTAL),
114
+ ),
115
+ f"{attr}_dw_stdev": ColSpec(
116
+ col_type=ColType.AGG,
117
+ expr=pl.col(f"{attr}_dw_var").sqrt(),
118
+ ),
119
+ f"{attr}_dwz": ColSpec(
120
+ col_type=ColType.AGG,
121
+ expr=pl.col(f"{attr}_dw_excess") / pl.col(f"{attr}_dw_stdev"),
122
+ ),
123
+ f"{attr}_pool_weight_group": ColSpec(
124
+ col_type=ColType.AGG,
125
+ expr=pl.col(f"{attr}") * pl.col(ColName.NUM_IN_POOL)
126
+ ),
127
+ f"{attr}_pool_weight_total": ColSpec(
128
+ col_type=ColType.AGG,
129
+ expr=pl.col(f"{attr}_pool_weight_group").sum()
130
+ ),
131
+ f"{attr}_pw_mean": ColSpec(
132
+ col_type=ColType.AGG,
133
+ expr=pl.col(f"{attr}_pool_weight_total") / pl.col(ColName.NUM_IN_POOL_TOTAL),
134
+ ),
135
+ f"{attr}_pw_excess": ColSpec(
136
+ col_type=ColType.AGG,
137
+ expr=pl.col(f"{attr}_pw_mean") - pl.col(f"{attr}"),
138
+ ),
139
+ f"{attr}_pw_var": ColSpec(
140
+ col_type=ColType.AGG,
141
+ expr=(pl.col(f"{attr}_pw_excess").pow(2) * pl.col(ColName.NUM_IN_POOL)) / pl.col(ColName.NUM_IN_POOL_TOTAL),
142
+ ),
143
+ f"{attr}_pw_stdev": ColSpec(
144
+ col_type=ColType.AGG,
145
+ expr=pl.col(f"{attr}_pw_var").sqrt(),
146
+ ),
147
+ f"{attr}_pwz": ColSpec(
148
+ col_type=ColType.AGG,
149
+ expr=pl.col(f"{attr}_pw_excess") / pl.col(f"{attr}_pw_stdev"),
150
+ ),
151
+ }
152
+
153
+ if not silent:
154
+ print_ext(ext)
155
+
156
+ return ext
157
+
158
+ def more(silent=True):
159
+ wr_bucket = pl.col(ColName.USER_GAME_WIN_RATE_BUCKET)
160
+ gp_bucket = pl.col(ColName.USER_N_GAMES_BUCKET)
161
+ ext = {
162
+ 'deq_base': ColSpec(
163
+ col_type=ColType.AGG,
164
+ expr=(pl.col("gp_wr_excess") + 0.03 * (1 - pl.col("ata") / 14).pow(2)) * pl.col("pct_gp")
165
+ ),
166
+ 'cohorts_plus': ColSpec(
167
+ col_type=ColType.GROUP_BY,
168
+ expr=pl.when((wr_bucket > 0.65) & (gp_bucket >= 500)).then(pl.lit('1 Best')).otherwise(
169
+ pl.when((wr_bucket > 0.61) & (gp_bucket >= 500) | (wr_bucket > 0.65) & (gp_bucket >= 100)).then(pl.lit('2 Elite')).otherwise(
170
+ pl.when((wr_bucket > 0.57) & (gp_bucket >= 100) | (wr_bucket > 0.61)).then(pl.lit('3 Competitive')).otherwise(
171
+ pl.when((wr_bucket > 0.53) & (gp_bucket >= 100) | (wr_bucket > 0.57)).then(pl.lit('4 Solid')).otherwise(pl.lit('5 None'))
172
+ )
173
+ )
174
+ )
175
+ )
176
+ }
177
+
178
+ if not silent:
179
+ print_ext(ext)
180
+
181
+ return ext
182
+
@@ -1,40 +0,0 @@
1
- import polars as pl
2
-
3
- from spells.enums import ColType
4
- from spells.columns import ColSpec
5
-
6
- def attr_metrics(attr):
7
- return {
8
- f"seen_{attr}": ColSpec(
9
- col_type=ColType.NAME_SUM,
10
- expr=(lambda name, card_context: pl.when(pl.col(f"pack_card_{name}") > 0)
11
- .then(card_context[name][attr])
12
- .otherwise(None)),
13
- ),
14
- f"pick_{attr}": ColSpec(
15
- col_type=ColType.PICK_SUM,
16
- expr=lambda name, card_context: card_context[name][attr]
17
- ),
18
- f"least_{attr}_taken": ColSpec(
19
- col_type=ColType.PICK_SUM,
20
- expr=(lambda names: pl.col(f'pick_{attr}')
21
- <= pl.min_horizontal([pl.col(f"seen_{attr}_{name}") for name in names])),
22
- ),
23
- f"least_{attr}_taken_rate": ColSpec(
24
- col_type=ColType.AGG,
25
- expr=pl.col(f"least_{attr}_taken") / pl.col("num_taken"),
26
- ),
27
- f"greatest_{attr}_taken": ColSpec(
28
- col_type=ColType.PICK_SUM,
29
- expr=(lambda names: pl.col(f'pick_{attr}')
30
- >= pl.max_horizontal([pl.col(f"seen_{attr}_{name}") for name in names])),
31
- ),
32
- f"greatest_{attr}_taken_rate": ColSpec(
33
- col_type=ColType.AGG,
34
- expr=pl.col(f"greatest_{attr}_taken") / pl.col("num_taken"),
35
- ),
36
- f"pick_{attr}_mean": ColSpec(
37
- col_type=ColType.AGG,
38
- expr=pl.col(f"pick_{attr}") / pl.col("num_taken")
39
- )
40
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes