spells-mtg 0.3.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- spells/__init__.py +2 -2
- spells/cards.py +4 -1
- spells/columns.py +56 -116
- spells/draft_data.py +106 -74
- spells/enums.py +4 -1
- spells/manifest.py +47 -29
- {spells_mtg-0.3.0.dist-info → spells_mtg-0.4.0.dist-info}/METADATA +40 -38
- spells_mtg-0.4.0.dist-info/RECORD +15 -0
- spells_mtg-0.3.0.dist-info/RECORD +0 -15
- {spells_mtg-0.3.0.dist-info → spells_mtg-0.4.0.dist-info}/WHEEL +0 -0
- {spells_mtg-0.3.0.dist-info → spells_mtg-0.4.0.dist-info}/entry_points.txt +0 -0
- {spells_mtg-0.3.0.dist-info → spells_mtg-0.4.0.dist-info}/licenses/LICENSE +0 -0
spells/__init__.py
CHANGED
spells/cards.py
CHANGED
|
@@ -76,7 +76,10 @@ def _extract_value(set_code: str, name: str, card_dict: dict, field: CardAttr):
|
|
|
76
76
|
|
|
77
77
|
def card_df(draft_set_code: str, names: list[str]) -> pl.DataFrame:
|
|
78
78
|
draft_set_json = _fetch_mtg_json(draft_set_code)
|
|
79
|
-
|
|
79
|
+
booster_info = draft_set_json["data"]["booster"]
|
|
80
|
+
|
|
81
|
+
booster_type = "play" if "play" in booster_info else "draft"
|
|
82
|
+
set_codes = booster_info[booster_type]["sourceSetCodes"]
|
|
80
83
|
set_codes.reverse()
|
|
81
84
|
|
|
82
85
|
card_data_map = {}
|
spells/columns.py
CHANGED
|
@@ -10,13 +10,9 @@ from spells.enums import View, ColName, ColType
|
|
|
10
10
|
class ColumnSpec:
|
|
11
11
|
name: str
|
|
12
12
|
col_type: ColType
|
|
13
|
-
expr: pl.Expr | None = None
|
|
14
|
-
exprMap: Callable[[str], pl.Expr] | None = None
|
|
13
|
+
expr: pl.Expr | Callable[..., pl.Expr] | None = None
|
|
15
14
|
views: list[View] | None = None
|
|
16
|
-
|
|
17
|
-
version: str | None = (
|
|
18
|
-
None # only needed for user-defined functions with python functions in expr
|
|
19
|
-
)
|
|
15
|
+
version: str | None = None
|
|
20
16
|
|
|
21
17
|
|
|
22
18
|
@dataclass(frozen=True)
|
|
@@ -25,7 +21,7 @@ class ColumnDefinition:
|
|
|
25
21
|
col_type: ColType
|
|
26
22
|
expr: pl.Expr | tuple[pl.Expr, ...]
|
|
27
23
|
views: set[View]
|
|
28
|
-
dependencies:
|
|
24
|
+
dependencies: set[str]
|
|
29
25
|
signature: str
|
|
30
26
|
|
|
31
27
|
|
|
@@ -49,7 +45,7 @@ _column_specs = [
|
|
|
49
45
|
ColumnSpec(
|
|
50
46
|
name=ColName.NAME,
|
|
51
47
|
col_type=ColType.GROUP_BY,
|
|
52
|
-
|
|
48
|
+
views=[View.CARD],
|
|
53
49
|
),
|
|
54
50
|
ColumnSpec(
|
|
55
51
|
name=ColName.EXPANSION,
|
|
@@ -74,26 +70,22 @@ _column_specs = [
|
|
|
74
70
|
ColumnSpec(
|
|
75
71
|
name=ColName.DRAFT_DATE,
|
|
76
72
|
col_type=ColType.GROUP_BY,
|
|
77
|
-
expr=pl.col(
|
|
78
|
-
dependencies=[ColName.DRAFT_TIME],
|
|
73
|
+
expr=pl.col(ColName.DRAFT_TIME).str.to_datetime("%Y-%m-%d %H:%M:%S").dt.date(),
|
|
79
74
|
),
|
|
80
75
|
ColumnSpec(
|
|
81
76
|
name=ColName.DRAFT_DAY_OF_WEEK,
|
|
82
77
|
col_type=ColType.GROUP_BY,
|
|
83
|
-
expr=pl.col(
|
|
84
|
-
dependencies=[ColName.DRAFT_TIME],
|
|
78
|
+
expr=pl.col(ColName.DRAFT_TIME).str.to_datetime("%Y-%m-%d %H:%M:%S").dt.weekday(),
|
|
85
79
|
),
|
|
86
80
|
ColumnSpec(
|
|
87
81
|
name=ColName.DRAFT_HOUR,
|
|
88
82
|
col_type=ColType.GROUP_BY,
|
|
89
|
-
expr=pl.col(
|
|
90
|
-
dependencies=[ColName.DRAFT_TIME],
|
|
83
|
+
expr=pl.col(ColName.DRAFT_TIME).str.to_datetime("%Y-%m-%d %H:%M:%S").dt.hour(),
|
|
91
84
|
),
|
|
92
85
|
ColumnSpec(
|
|
93
86
|
name=ColName.DRAFT_WEEK,
|
|
94
87
|
col_type=ColType.GROUP_BY,
|
|
95
|
-
expr=pl.col(
|
|
96
|
-
dependencies=[ColName.DRAFT_TIME],
|
|
88
|
+
expr=pl.col(ColName.DRAFT_TIME).str.to_datetime("%Y-%m-%d %H:%M:%S").dt.week(),
|
|
97
89
|
),
|
|
98
90
|
ColumnSpec(
|
|
99
91
|
name=ColName.RANK,
|
|
@@ -113,18 +105,17 @@ _column_specs = [
|
|
|
113
105
|
ColumnSpec(
|
|
114
106
|
name=ColName.PLAYER_COHORT,
|
|
115
107
|
col_type=ColType.GROUP_BY,
|
|
116
|
-
expr=pl.when(pl.col(
|
|
108
|
+
expr=pl.when(pl.col(ColName.USER_N_GAMES_BUCKET) < 100)
|
|
117
109
|
.then(pl.lit("Other"))
|
|
118
110
|
.otherwise(
|
|
119
|
-
pl.when(pl.col(
|
|
111
|
+
pl.when(pl.col(ColName.USER_GAME_WIN_RATE_BUCKET) > 0.57)
|
|
120
112
|
.then(pl.lit("Top"))
|
|
121
113
|
.otherwise(
|
|
122
|
-
pl.when(pl.col(
|
|
114
|
+
pl.when(pl.col(ColName.USER_GAME_WIN_RATE_BUCKET) < 0.49)
|
|
123
115
|
.then(pl.lit("Bottom"))
|
|
124
116
|
.otherwise(pl.lit("Middle"))
|
|
125
117
|
)
|
|
126
118
|
),
|
|
127
|
-
dependencies=[ColName.USER_N_GAMES_BUCKET, ColName.USER_GAME_WIN_RATE_BUCKET],
|
|
128
119
|
),
|
|
129
120
|
ColumnSpec(
|
|
130
121
|
name=ColName.EVENT_MATCH_WINS,
|
|
@@ -136,7 +127,6 @@ _column_specs = [
|
|
|
136
127
|
col_type=ColType.PICK_SUM,
|
|
137
128
|
views=[View.DRAFT],
|
|
138
129
|
expr=pl.col(ColName.EVENT_MATCH_WINS),
|
|
139
|
-
dependencies=[ColName.EVENT_MATCH_WINS],
|
|
140
130
|
),
|
|
141
131
|
ColumnSpec(
|
|
142
132
|
name=ColName.EVENT_MATCH_LOSSES,
|
|
@@ -147,33 +137,28 @@ _column_specs = [
|
|
|
147
137
|
name=ColName.EVENT_MATCH_LOSSES_SUM,
|
|
148
138
|
col_type=ColType.PICK_SUM,
|
|
149
139
|
expr=pl.col(ColName.EVENT_MATCH_LOSSES),
|
|
150
|
-
dependencies=[ColName.EVENT_MATCH_LOSSES],
|
|
151
140
|
),
|
|
152
141
|
ColumnSpec(
|
|
153
142
|
name=ColName.EVENT_MATCHES,
|
|
154
143
|
col_type=ColType.GROUP_BY,
|
|
155
|
-
expr=pl.col(
|
|
156
|
-
dependencies=[ColName.EVENT_MATCH_WINS, ColName.EVENT_MATCH_LOSSES],
|
|
144
|
+
expr=pl.col(ColName.EVENT_MATCH_WINS) + pl.col(ColName.EVENT_MATCH_LOSSES),
|
|
157
145
|
),
|
|
158
146
|
ColumnSpec(
|
|
159
147
|
name=ColName.EVENT_MATCHES_SUM,
|
|
160
148
|
col_type=ColType.PICK_SUM,
|
|
161
149
|
expr=pl.col(ColName.EVENT_MATCHES),
|
|
162
|
-
dependencies=[ColName.EVENT_MATCHES],
|
|
163
150
|
),
|
|
164
151
|
ColumnSpec(
|
|
165
152
|
name=ColName.IS_TROPHY,
|
|
166
153
|
col_type=ColType.GROUP_BY,
|
|
167
|
-
expr=pl.when(pl.col(
|
|
168
|
-
.then(pl.col(
|
|
169
|
-
.otherwise(pl.col(
|
|
170
|
-
dependencies=[ColName.EVENT_TYPE, ColName.EVENT_MATCH_WINS],
|
|
154
|
+
expr=pl.when(pl.col(ColName.EVENT_TYPE) == "Traditional")
|
|
155
|
+
.then(pl.col(ColName.EVENT_MATCH_WINS) == 3)
|
|
156
|
+
.otherwise(pl.col(ColName.EVENT_MATCH_WINS) == 7),
|
|
171
157
|
),
|
|
172
158
|
ColumnSpec(
|
|
173
159
|
name=ColName.IS_TROPHY_SUM,
|
|
174
160
|
col_type=ColType.PICK_SUM,
|
|
175
161
|
expr=pl.col(ColName.IS_TROPHY),
|
|
176
|
-
dependencies=[ColName.IS_TROPHY],
|
|
177
162
|
),
|
|
178
163
|
ColumnSpec(
|
|
179
164
|
name=ColName.PACK_NUMBER,
|
|
@@ -183,8 +168,7 @@ _column_specs = [
|
|
|
183
168
|
ColumnSpec(
|
|
184
169
|
name=ColName.PACK_NUM,
|
|
185
170
|
col_type=ColType.GROUP_BY,
|
|
186
|
-
expr=pl.col(
|
|
187
|
-
dependencies=[ColName.PACK_NUMBER],
|
|
171
|
+
expr=pl.col(ColName.PACK_NUMBER) + 1,
|
|
188
172
|
),
|
|
189
173
|
ColumnSpec(
|
|
190
174
|
name=ColName.PICK_NUMBER,
|
|
@@ -194,26 +178,23 @@ _column_specs = [
|
|
|
194
178
|
ColumnSpec(
|
|
195
179
|
name=ColName.PICK_NUM,
|
|
196
180
|
col_type=ColType.GROUP_BY,
|
|
197
|
-
expr=pl.col(
|
|
198
|
-
dependencies=[ColName.PICK_NUMBER],
|
|
181
|
+
expr=pl.col(ColName.PICK_NUMBER) + 1,
|
|
199
182
|
),
|
|
200
183
|
ColumnSpec(
|
|
201
184
|
name=ColName.TAKEN_AT,
|
|
202
185
|
col_type=ColType.PICK_SUM,
|
|
203
186
|
expr=pl.col(ColName.PICK_NUM),
|
|
204
|
-
dependencies=[ColName.PICK_NUM],
|
|
205
187
|
),
|
|
206
188
|
ColumnSpec(
|
|
207
189
|
name=ColName.NUM_TAKEN,
|
|
208
190
|
col_type=ColType.PICK_SUM,
|
|
209
191
|
expr=pl.when(pl.col(ColName.PICK).is_not_null())
|
|
210
192
|
.then(1)
|
|
211
|
-
.otherwise(0),
|
|
212
|
-
dependencies=[ColName.PICK],
|
|
193
|
+
.otherwise(0),
|
|
213
194
|
),
|
|
214
195
|
ColumnSpec(
|
|
215
196
|
name=ColName.PICK,
|
|
216
|
-
col_type=ColType.FILTER_ONLY,
|
|
197
|
+
col_type=ColType.FILTER_ONLY,
|
|
217
198
|
views=[View.DRAFT],
|
|
218
199
|
),
|
|
219
200
|
ColumnSpec(
|
|
@@ -234,15 +215,13 @@ _column_specs = [
|
|
|
234
215
|
ColumnSpec(
|
|
235
216
|
name=ColName.LAST_SEEN,
|
|
236
217
|
col_type=ColType.NAME_SUM,
|
|
237
|
-
|
|
238
|
-
* pl.min_horizontal(
|
|
239
|
-
dependencies=[ColName.PACK_CARD, ColName.PICK_NUM],
|
|
218
|
+
expr=lambda name: pl.col(f"pack_card_{name}")
|
|
219
|
+
* pl.min_horizontal(ColName.PICK_NUM, 8),
|
|
240
220
|
),
|
|
241
221
|
ColumnSpec(
|
|
242
222
|
name=ColName.NUM_SEEN,
|
|
243
223
|
col_type=ColType.NAME_SUM,
|
|
244
|
-
|
|
245
|
-
dependencies=[ColName.PACK_CARD, ColName.PICK_NUM],
|
|
224
|
+
expr=lambda name: pl.col(f"pack_card_{name}") * (pl.col(ColName.PICK_NUM) <= 8),
|
|
246
225
|
),
|
|
247
226
|
ColumnSpec(
|
|
248
227
|
name=ColName.POOL,
|
|
@@ -257,26 +236,22 @@ _column_specs = [
|
|
|
257
236
|
ColumnSpec(
|
|
258
237
|
name=ColName.GAME_DATE,
|
|
259
238
|
col_type=ColType.GROUP_BY,
|
|
260
|
-
expr=pl.col(
|
|
261
|
-
dependencies=[ColName.GAME_TIME],
|
|
239
|
+
expr=pl.col(ColName.GAME_TIME).str.to_datetime("%Y-%m-%d %H-%M-%S").dt.date(),
|
|
262
240
|
),
|
|
263
241
|
ColumnSpec(
|
|
264
242
|
name=ColName.GAME_DAY_OF_WEEK,
|
|
265
243
|
col_type=ColType.GROUP_BY,
|
|
266
|
-
expr=pl.col(
|
|
267
|
-
dependencies=[ColName.GAME_TIME],
|
|
244
|
+
expr=pl.col(ColName.GAME_TIME).str.to_datetime("%Y-%m-%d %H-%M-%S").dt.weekday(),
|
|
268
245
|
),
|
|
269
246
|
ColumnSpec(
|
|
270
247
|
name=ColName.GAME_HOUR,
|
|
271
248
|
col_type=ColType.GROUP_BY,
|
|
272
|
-
expr=pl.col(
|
|
273
|
-
dependencies=[ColName.GAME_TIME],
|
|
249
|
+
expr=pl.col(ColName.GAME_TIME).str.to_datetime("%Y-%m-%d %H-%M-%S").dt.hour(),
|
|
274
250
|
),
|
|
275
251
|
ColumnSpec(
|
|
276
252
|
name=ColName.GAME_WEEK,
|
|
277
253
|
col_type=ColType.GROUP_BY,
|
|
278
|
-
expr=pl.col(
|
|
279
|
-
dependencies=[ColName.GAME_TIME],
|
|
254
|
+
expr=pl.col(ColName.GAME_TIME).str.to_datetime("%Y-%m-%d %H-%M-%S").dt.week(),
|
|
280
255
|
),
|
|
281
256
|
ColumnSpec(
|
|
282
257
|
name=ColName.BUILD_INDEX,
|
|
@@ -297,19 +272,16 @@ _column_specs = [
|
|
|
297
272
|
name=ColName.NUM_GAMES,
|
|
298
273
|
col_type=ColType.GAME_SUM,
|
|
299
274
|
expr=pl.col(ColName.GAME_NUMBER).is_not_null(),
|
|
300
|
-
dependencies=[ColName.GAME_NUMBER],
|
|
301
275
|
),
|
|
302
276
|
ColumnSpec(
|
|
303
277
|
name=ColName.NUM_MATCHES,
|
|
304
278
|
col_type=ColType.GAME_SUM,
|
|
305
279
|
expr=pl.col(ColName.GAME_NUMBER) == 1,
|
|
306
|
-
dependencies=[ColName.GAME_NUMBER],
|
|
307
280
|
),
|
|
308
281
|
ColumnSpec(
|
|
309
282
|
name=ColName.NUM_EVENTS,
|
|
310
283
|
col_type=ColType.GAME_SUM,
|
|
311
284
|
expr=(pl.col(ColName.GAME_NUMBER) == 1) & (pl.col(ColName.MATCH_NUMBER) == 1),
|
|
312
|
-
dependencies=[ColName.GAME_NUMBER, ColName.MATCH_NUMBER],
|
|
313
285
|
),
|
|
314
286
|
ColumnSpec(
|
|
315
287
|
name=ColName.OPP_RANK,
|
|
@@ -325,7 +297,6 @@ _column_specs = [
|
|
|
325
297
|
name=ColName.NUM_COLORS,
|
|
326
298
|
col_type=ColType.GROUP_BY,
|
|
327
299
|
expr=pl.col(ColName.MAIN_COLORS).str.len_chars(),
|
|
328
|
-
dependencies=[ColName.MAIN_COLORS],
|
|
329
300
|
),
|
|
330
301
|
ColumnSpec(
|
|
331
302
|
name=ColName.SPLASH_COLORS,
|
|
@@ -336,7 +307,6 @@ _column_specs = [
|
|
|
336
307
|
name=ColName.HAS_SPLASH,
|
|
337
308
|
col_type=ColType.GROUP_BY,
|
|
338
309
|
expr=pl.col(ColName.SPLASH_COLORS).str.len_chars() > 0,
|
|
339
|
-
dependencies=[ColName.SPLASH_COLORS],
|
|
340
310
|
),
|
|
341
311
|
ColumnSpec(
|
|
342
312
|
name=ColName.ON_PLAY,
|
|
@@ -347,7 +317,6 @@ _column_specs = [
|
|
|
347
317
|
name=ColName.NUM_ON_PLAY,
|
|
348
318
|
col_type=ColType.GAME_SUM,
|
|
349
319
|
expr=pl.col(ColName.ON_PLAY),
|
|
350
|
-
dependencies=[ColName.ON_PLAY],
|
|
351
320
|
),
|
|
352
321
|
ColumnSpec(
|
|
353
322
|
name=ColName.NUM_MULLIGANS,
|
|
@@ -358,7 +327,6 @@ _column_specs = [
|
|
|
358
327
|
name=ColName.NUM_MULLIGANS_SUM,
|
|
359
328
|
col_type=ColType.GAME_SUM,
|
|
360
329
|
expr=pl.col(ColName.NUM_MULLIGANS),
|
|
361
|
-
dependencies=[ColName.NUM_MULLIGANS],
|
|
362
330
|
),
|
|
363
331
|
ColumnSpec(
|
|
364
332
|
name=ColName.OPP_NUM_MULLIGANS,
|
|
@@ -369,7 +337,6 @@ _column_specs = [
|
|
|
369
337
|
name=ColName.OPP_NUM_MULLIGANS_SUM,
|
|
370
338
|
col_type=ColType.GAME_SUM,
|
|
371
339
|
expr=pl.col(ColName.OPP_NUM_MULLIGANS),
|
|
372
|
-
dependencies=[ColName.OPP_NUM_MULLIGANS],
|
|
373
340
|
),
|
|
374
341
|
ColumnSpec(
|
|
375
342
|
name=ColName.OPP_COLORS,
|
|
@@ -385,7 +352,6 @@ _column_specs = [
|
|
|
385
352
|
name=ColName.NUM_TURNS_SUM,
|
|
386
353
|
col_type=ColType.GAME_SUM,
|
|
387
354
|
expr=pl.col(ColName.NUM_TURNS),
|
|
388
|
-
dependencies=[ColName.NUM_TURNS],
|
|
389
355
|
),
|
|
390
356
|
ColumnSpec(
|
|
391
357
|
name=ColName.WON,
|
|
@@ -396,7 +362,6 @@ _column_specs = [
|
|
|
396
362
|
name=ColName.NUM_WON,
|
|
397
363
|
col_type=ColType.GAME_SUM,
|
|
398
364
|
expr=pl.col(ColName.WON),
|
|
399
|
-
dependencies=[ColName.WON],
|
|
400
365
|
),
|
|
401
366
|
ColumnSpec(
|
|
402
367
|
name=ColName.OPENING_HAND,
|
|
@@ -406,8 +371,7 @@ _column_specs = [
|
|
|
406
371
|
ColumnSpec(
|
|
407
372
|
name=ColName.WON_OPENING_HAND,
|
|
408
373
|
col_type=ColType.NAME_SUM,
|
|
409
|
-
|
|
410
|
-
dependencies=[ColName.OPENING_HAND, ColName.WON],
|
|
374
|
+
expr=lambda name: pl.col(f"opening_hand_{name}") * pl.col(ColName.WON),
|
|
411
375
|
),
|
|
412
376
|
ColumnSpec(
|
|
413
377
|
name=ColName.DRAWN,
|
|
@@ -417,8 +381,7 @@ _column_specs = [
|
|
|
417
381
|
ColumnSpec(
|
|
418
382
|
name=ColName.WON_DRAWN,
|
|
419
383
|
col_type=ColType.NAME_SUM,
|
|
420
|
-
|
|
421
|
-
dependencies=[ColName.DRAWN, ColName.WON],
|
|
384
|
+
expr=lambda name: pl.col(f"drawn_{name}") * pl.col(ColName.WON),
|
|
422
385
|
),
|
|
423
386
|
ColumnSpec(
|
|
424
387
|
name=ColName.TUTORED,
|
|
@@ -428,8 +391,7 @@ _column_specs = [
|
|
|
428
391
|
ColumnSpec(
|
|
429
392
|
name=ColName.WON_TUTORED,
|
|
430
393
|
col_type=ColType.NAME_SUM,
|
|
431
|
-
|
|
432
|
-
dependencies=[ColName.TUTORED, ColName.WON],
|
|
394
|
+
expr=lambda name: pl.col(f"tutored_{name}") * pl.col(ColName.WON),
|
|
433
395
|
),
|
|
434
396
|
ColumnSpec(
|
|
435
397
|
name=ColName.DECK,
|
|
@@ -439,8 +401,7 @@ _column_specs = [
|
|
|
439
401
|
ColumnSpec(
|
|
440
402
|
name=ColName.WON_DECK,
|
|
441
403
|
col_type=ColType.NAME_SUM,
|
|
442
|
-
|
|
443
|
-
dependencies=[ColName.DECK, ColName.WON],
|
|
404
|
+
expr=lambda name: pl.col(f"deck_{name}") * pl.col(ColName.WON),
|
|
444
405
|
),
|
|
445
406
|
ColumnSpec(
|
|
446
407
|
name=ColName.SIDEBOARD,
|
|
@@ -450,31 +411,23 @@ _column_specs = [
|
|
|
450
411
|
ColumnSpec(
|
|
451
412
|
name=ColName.WON_SIDEBOARD,
|
|
452
413
|
col_type=ColType.NAME_SUM,
|
|
453
|
-
|
|
454
|
-
dependencies=[ColName.SIDEBOARD, ColName.WON],
|
|
414
|
+
expr=lambda name: pl.col(f"sideboard_{name}") * pl.col(ColName.WON),
|
|
455
415
|
),
|
|
456
416
|
ColumnSpec(
|
|
457
417
|
name=ColName.NUM_GNS,
|
|
458
418
|
col_type=ColType.NAME_SUM,
|
|
459
|
-
|
|
419
|
+
expr=lambda name: pl.max_horizontal(
|
|
460
420
|
0,
|
|
461
421
|
pl.col(f"deck_{name}")
|
|
462
422
|
- pl.col(f"drawn_{name}")
|
|
463
423
|
- pl.col(f"tutored_{name}")
|
|
464
424
|
- pl.col(f"opening_hand_{name}"),
|
|
465
425
|
),
|
|
466
|
-
dependencies=[
|
|
467
|
-
ColName.DECK,
|
|
468
|
-
ColName.DRAWN,
|
|
469
|
-
ColName.TUTORED,
|
|
470
|
-
ColName.OPENING_HAND,
|
|
471
|
-
],
|
|
472
426
|
),
|
|
473
427
|
ColumnSpec(
|
|
474
428
|
name=ColName.WON_NUM_GNS,
|
|
475
429
|
col_type=ColType.NAME_SUM,
|
|
476
|
-
|
|
477
|
-
dependencies=[ColName.NUM_GNS, ColName.WON],
|
|
430
|
+
expr=lambda name: pl.col(ColName.WON) * pl.col(f"num_gns_{name}"),
|
|
478
431
|
),
|
|
479
432
|
ColumnSpec(
|
|
480
433
|
name=ColName.SET_CODE,
|
|
@@ -506,9 +459,18 @@ _column_specs = [
|
|
|
506
459
|
),
|
|
507
460
|
ColumnSpec(
|
|
508
461
|
name=ColName.DECK_MANA_VALUE,
|
|
509
|
-
col_type=ColType.
|
|
510
|
-
expr=
|
|
511
|
-
|
|
462
|
+
col_type=ColType.NAME_SUM,
|
|
463
|
+
expr=lambda name, card_context: card_context[name][ColName.MANA_VALUE] * pl.col(f"deck_{name}"),
|
|
464
|
+
),
|
|
465
|
+
ColumnSpec(
|
|
466
|
+
name=ColName.DECK_LANDS,
|
|
467
|
+
col_type=ColType.NAME_SUM,
|
|
468
|
+
expr=lambda name, card_context: pl.col(f"deck_{name}") * ( 1 if 'Land' in card_context[name][ColName.CARD_TYPE] else 0 )
|
|
469
|
+
),
|
|
470
|
+
ColumnSpec(
|
|
471
|
+
name=ColName.DECK_SPELLS,
|
|
472
|
+
col_type=ColType.NAME_SUM,
|
|
473
|
+
expr=lambda name: pl.col(f"deck_{name}") - pl.col(f"deck_lands_{name}"),
|
|
512
474
|
),
|
|
513
475
|
ColumnSpec(
|
|
514
476
|
name=ColName.MANA_COST,
|
|
@@ -542,196 +504,174 @@ _column_specs = [
|
|
|
542
504
|
name=ColName.PICKED_MATCH_WR,
|
|
543
505
|
col_type=ColType.AGG,
|
|
544
506
|
expr=pl.col(ColName.EVENT_MATCH_WINS_SUM) / pl.col(ColName.EVENT_MATCHES),
|
|
545
|
-
dependencies=[ColName.EVENT_MATCH_WINS_SUM, ColName.EVENT_MATCHES],
|
|
546
507
|
),
|
|
547
508
|
ColumnSpec(
|
|
548
509
|
name=ColName.TROPHY_RATE,
|
|
549
510
|
col_type=ColType.AGG,
|
|
550
511
|
expr=pl.col(ColName.IS_TROPHY_SUM) / pl.col(ColName.NUM_TAKEN),
|
|
551
|
-
dependencies=[ColName.IS_TROPHY_SUM, ColName.NUM_TAKEN],
|
|
552
512
|
),
|
|
553
513
|
ColumnSpec(
|
|
554
514
|
name=ColName.GAME_WR,
|
|
555
515
|
col_type=ColType.AGG,
|
|
556
516
|
expr=pl.col(ColName.NUM_WON) / pl.col(ColName.NUM_GAMES),
|
|
557
|
-
dependencies=[ColName.NUM_WON, ColName.NUM_GAMES],
|
|
558
517
|
),
|
|
559
518
|
ColumnSpec(
|
|
560
519
|
name=ColName.ALSA,
|
|
561
520
|
col_type=ColType.AGG,
|
|
562
521
|
expr=pl.col(ColName.LAST_SEEN) / pl.col(ColName.NUM_SEEN),
|
|
563
|
-
dependencies=[ColName.LAST_SEEN, ColName.NUM_SEEN],
|
|
564
522
|
),
|
|
565
523
|
ColumnSpec(
|
|
566
524
|
name=ColName.ATA,
|
|
567
525
|
col_type=ColType.AGG,
|
|
568
526
|
expr=pl.col(ColName.TAKEN_AT) / pl.col(ColName.NUM_TAKEN),
|
|
569
|
-
dependencies=[ColName.TAKEN_AT, ColName.NUM_TAKEN],
|
|
570
527
|
),
|
|
571
528
|
ColumnSpec(
|
|
572
529
|
name=ColName.NUM_GP,
|
|
573
530
|
col_type=ColType.AGG,
|
|
574
531
|
expr=pl.col(ColName.DECK),
|
|
575
|
-
dependencies=[ColName.DECK],
|
|
576
532
|
),
|
|
577
533
|
ColumnSpec(
|
|
578
534
|
name=ColName.PCT_GP,
|
|
579
535
|
col_type=ColType.AGG,
|
|
580
536
|
expr=pl.col(ColName.DECK) / (pl.col(ColName.DECK) + pl.col(ColName.SIDEBOARD)),
|
|
581
|
-
dependencies=[ColName.DECK, ColName.SIDEBOARD],
|
|
582
537
|
),
|
|
583
538
|
ColumnSpec(
|
|
584
539
|
name=ColName.GP_WR,
|
|
585
540
|
col_type=ColType.AGG,
|
|
586
541
|
expr=pl.col(ColName.WON_DECK) / pl.col(ColName.DECK),
|
|
587
|
-
dependencies=[ColName.WON_DECK, ColName.DECK],
|
|
588
542
|
),
|
|
589
543
|
ColumnSpec(
|
|
590
544
|
name=ColName.NUM_OH,
|
|
591
545
|
col_type=ColType.AGG,
|
|
592
546
|
expr=pl.col(ColName.OPENING_HAND),
|
|
593
|
-
dependencies=[ColName.OPENING_HAND],
|
|
594
547
|
),
|
|
595
548
|
ColumnSpec(
|
|
596
549
|
name=ColName.OH_WR,
|
|
597
550
|
col_type=ColType.AGG,
|
|
598
551
|
expr=pl.col(ColName.WON_OPENING_HAND) / pl.col(ColName.OPENING_HAND),
|
|
599
|
-
dependencies=[ColName.WON_OPENING_HAND, ColName.OPENING_HAND],
|
|
600
552
|
),
|
|
601
553
|
ColumnSpec(
|
|
602
554
|
name=ColName.NUM_GIH,
|
|
603
555
|
col_type=ColType.AGG,
|
|
604
556
|
expr=pl.col(ColName.OPENING_HAND) + pl.col(ColName.DRAWN),
|
|
605
|
-
dependencies=[ColName.OPENING_HAND, ColName.DRAWN],
|
|
606
557
|
),
|
|
607
558
|
ColumnSpec(
|
|
608
559
|
name=ColName.NUM_GIH_WON,
|
|
609
560
|
col_type=ColType.AGG,
|
|
610
561
|
expr=pl.col(ColName.WON_OPENING_HAND) + pl.col(ColName.WON_DRAWN),
|
|
611
|
-
dependencies=[ColName.WON_OPENING_HAND, ColName.WON_DRAWN],
|
|
612
562
|
),
|
|
613
563
|
ColumnSpec(
|
|
614
564
|
name=ColName.GIH_WR,
|
|
615
565
|
col_type=ColType.AGG,
|
|
616
566
|
expr=pl.col(ColName.NUM_GIH_WON) / pl.col(ColName.NUM_GIH),
|
|
617
|
-
dependencies=[ColName.NUM_GIH_WON, ColName.NUM_GIH],
|
|
618
567
|
),
|
|
619
568
|
ColumnSpec(
|
|
620
569
|
name=ColName.GNS_WR,
|
|
621
570
|
col_type=ColType.AGG,
|
|
622
571
|
expr=pl.col(ColName.WON_NUM_GNS) / pl.col(ColName.NUM_GNS),
|
|
623
|
-
dependencies=[ColName.WON_NUM_GNS, ColName.NUM_GNS],
|
|
624
572
|
),
|
|
625
573
|
ColumnSpec(
|
|
626
574
|
name=ColName.IWD,
|
|
627
575
|
col_type=ColType.AGG,
|
|
628
576
|
expr=pl.col(ColName.GIH_WR) - pl.col(ColName.GNS_WR),
|
|
629
|
-
dependencies=[ColName.GIH_WR, ColName.GNS_WR],
|
|
630
577
|
),
|
|
631
578
|
ColumnSpec(
|
|
632
579
|
name=ColName.NUM_IN_POOL,
|
|
633
580
|
col_type=ColType.AGG,
|
|
634
581
|
expr=pl.col(ColName.DECK) + pl.col(ColName.SIDEBOARD),
|
|
635
|
-
dependencies=[ColName.DECK, ColName.SIDEBOARD],
|
|
636
582
|
),
|
|
637
583
|
ColumnSpec(
|
|
638
584
|
name=ColName.IN_POOL_WR,
|
|
639
585
|
col_type=ColType.AGG,
|
|
640
586
|
expr=(pl.col(ColName.WON_DECK) + pl.col(ColName.WON_SIDEBOARD))
|
|
641
587
|
/ pl.col(ColName.NUM_IN_POOL),
|
|
642
|
-
dependencies=[ColName.WON_DECK, ColName.WON_SIDEBOARD, ColName.NUM_IN_POOL],
|
|
643
588
|
),
|
|
644
589
|
ColumnSpec(
|
|
645
590
|
name=ColName.DECK_TOTAL,
|
|
646
591
|
col_type=ColType.AGG,
|
|
647
592
|
expr=pl.col(ColName.DECK).sum(),
|
|
648
|
-
dependencies=[ColName.DECK],
|
|
649
593
|
),
|
|
650
594
|
ColumnSpec(
|
|
651
595
|
name=ColName.WON_DECK_TOTAL,
|
|
652
596
|
col_type=ColType.AGG,
|
|
653
597
|
expr=pl.col(ColName.WON_DECK).sum(),
|
|
654
|
-
dependencies=[ColName.WON_DECK],
|
|
655
598
|
),
|
|
656
599
|
ColumnSpec(
|
|
657
600
|
name=ColName.GP_WR_MEAN,
|
|
658
601
|
col_type=ColType.AGG,
|
|
659
602
|
expr=pl.col(ColName.WON_DECK_TOTAL) / pl.col(ColName.DECK_TOTAL),
|
|
660
|
-
dependencies=[ColName.WON_DECK_TOTAL, ColName.DECK_TOTAL],
|
|
661
603
|
),
|
|
662
604
|
ColumnSpec(
|
|
663
605
|
name=ColName.GP_WR_EXCESS,
|
|
664
606
|
col_type=ColType.AGG,
|
|
665
607
|
expr=pl.col(ColName.GP_WR) - pl.col(ColName.GP_WR_MEAN),
|
|
666
|
-
dependencies=[ColName.GP_WR, ColName.GP_WR_MEAN],
|
|
667
608
|
),
|
|
668
609
|
ColumnSpec(
|
|
669
610
|
name=ColName.GP_WR_VAR,
|
|
670
611
|
col_type=ColType.AGG,
|
|
671
612
|
expr=(pl.col(ColName.GP_WR_EXCESS).pow(2) * pl.col(ColName.NUM_GP)).sum()
|
|
672
613
|
/ pl.col(ColName.DECK_TOTAL),
|
|
673
|
-
dependencies=[ColName.GP_WR_EXCESS, ColName.NUM_GP, ColName.DECK_TOTAL],
|
|
674
614
|
),
|
|
675
615
|
ColumnSpec(
|
|
676
616
|
name=ColName.GP_WR_STDEV,
|
|
677
617
|
col_type=ColType.AGG,
|
|
678
618
|
expr=pl.col(ColName.GP_WR_VAR).sqrt(),
|
|
679
|
-
dependencies=[ColName.GP_WR_VAR],
|
|
680
619
|
),
|
|
681
620
|
ColumnSpec(
|
|
682
621
|
name=ColName.GP_WR_Z,
|
|
683
622
|
col_type=ColType.AGG,
|
|
684
623
|
expr=pl.col(ColName.GP_WR_EXCESS) / pl.col(ColName.GP_WR_STDEV),
|
|
685
|
-
dependencies=[ColName.GP_WR_EXCESS, ColName.GP_WR_STDEV],
|
|
686
624
|
),
|
|
687
625
|
ColumnSpec(
|
|
688
626
|
name=ColName.GIH_TOTAL,
|
|
689
627
|
col_type=ColType.AGG,
|
|
690
628
|
expr=pl.col(ColName.NUM_GIH).sum(),
|
|
691
|
-
dependencies=[ColName.NUM_GIH],
|
|
692
629
|
),
|
|
693
630
|
ColumnSpec(
|
|
694
631
|
name=ColName.WON_GIH_TOTAL,
|
|
695
632
|
col_type=ColType.AGG,
|
|
696
633
|
expr=pl.col(ColName.NUM_GIH_WON).sum(),
|
|
697
|
-
dependencies=[ColName.NUM_GIH_WON],
|
|
698
634
|
),
|
|
699
635
|
ColumnSpec(
|
|
700
636
|
name=ColName.GIH_WR_MEAN,
|
|
701
637
|
col_type=ColType.AGG,
|
|
702
638
|
expr=pl.col(ColName.WON_GIH_TOTAL) / pl.col(ColName.GIH_TOTAL),
|
|
703
|
-
dependencies=[ColName.WON_GIH_TOTAL, ColName.GIH_TOTAL],
|
|
704
639
|
),
|
|
705
640
|
ColumnSpec(
|
|
706
641
|
name=ColName.GIH_WR_EXCESS,
|
|
707
642
|
col_type=ColType.AGG,
|
|
708
643
|
expr=pl.col(ColName.GIH_WR) - pl.col(ColName.GIH_WR_MEAN),
|
|
709
|
-
dependencies=[ColName.GIH_WR, ColName.GIH_WR_MEAN],
|
|
710
644
|
),
|
|
711
645
|
ColumnSpec(
|
|
712
646
|
name=ColName.GIH_WR_VAR,
|
|
713
647
|
col_type=ColType.AGG,
|
|
714
648
|
expr=(pl.col(ColName.GIH_WR_EXCESS).pow(2) * pl.col(ColName.NUM_GIH)).sum()
|
|
715
649
|
/ pl.col(ColName.GIH_TOTAL),
|
|
716
|
-
dependencies=[ColName.GIH_WR_EXCESS, ColName.NUM_GIH, ColName.GIH_TOTAL],
|
|
717
650
|
),
|
|
718
651
|
ColumnSpec(
|
|
719
652
|
name=ColName.GIH_WR_STDEV,
|
|
720
653
|
col_type=ColType.AGG,
|
|
721
654
|
expr=pl.col(ColName.GIH_WR_VAR).sqrt(),
|
|
722
|
-
dependencies=[ColName.GIH_WR_VAR],
|
|
723
655
|
),
|
|
724
656
|
ColumnSpec(
|
|
725
657
|
name=ColName.GIH_WR_Z,
|
|
726
658
|
col_type=ColType.AGG,
|
|
727
659
|
expr=pl.col(ColName.GIH_WR_EXCESS) / pl.col(ColName.GIH_WR_STDEV),
|
|
728
|
-
dependencies=[ColName.GIH_WR_EXCESS, ColName.GIH_WR_STDEV],
|
|
729
660
|
),
|
|
730
661
|
ColumnSpec(
|
|
731
662
|
name=ColName.DECK_MANA_VALUE_AVG,
|
|
732
663
|
col_type=ColType.AGG,
|
|
733
|
-
expr=pl.col(ColName.DECK_MANA_VALUE) / pl.col(ColName.
|
|
734
|
-
|
|
664
|
+
expr=pl.col(ColName.DECK_MANA_VALUE) / pl.col(ColName.DECK_SPELLS),
|
|
665
|
+
),
|
|
666
|
+
ColumnSpec(
|
|
667
|
+
name=ColName.DECK_LANDS_AVG,
|
|
668
|
+
col_type=ColType.AGG,
|
|
669
|
+
expr=pl.col(ColName.DECK_LANDS) / pl.col(ColName.NUM_GAMES),
|
|
670
|
+
),
|
|
671
|
+
ColumnSpec(
|
|
672
|
+
name=ColName.DECK_SPELLS_AVG,
|
|
673
|
+
col_type=ColType.AGG,
|
|
674
|
+
expr=pl.col(ColName.DECK_SPELLS) / pl.col(ColName.NUM_GAMES),
|
|
735
675
|
),
|
|
736
676
|
]
|
|
737
677
|
|
spells/draft_data.py
CHANGED
|
@@ -10,7 +10,8 @@ import datetime
|
|
|
10
10
|
import functools
|
|
11
11
|
import hashlib
|
|
12
12
|
import re
|
|
13
|
-
from
|
|
13
|
+
from inspect import signature
|
|
14
|
+
from typing import Callable, TypeVar, Any
|
|
14
15
|
|
|
15
16
|
import polars as pl
|
|
16
17
|
|
|
@@ -33,7 +34,7 @@ def _cache_key(args) -> str:
|
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
@functools.lru_cache(maxsize=None)
|
|
36
|
-
def _get_names(set_code: str) ->
|
|
37
|
+
def _get_names(set_code: str) -> list[str]:
|
|
37
38
|
card_fp = data_file_path(set_code, View.CARD)
|
|
38
39
|
card_view = pl.read_parquet(card_fp)
|
|
39
40
|
card_names_set = frozenset(card_view.get_column("name").to_list())
|
|
@@ -43,7 +44,7 @@ def _get_names(set_code: str) -> tuple[str, ...]:
|
|
|
43
44
|
cols = draft_view.collect_schema().names()
|
|
44
45
|
|
|
45
46
|
prefix = "pack_card_"
|
|
46
|
-
names =
|
|
47
|
+
names = [col[len(prefix) :] for col in cols if col.startswith(prefix)]
|
|
47
48
|
draft_names_set = frozenset(names)
|
|
48
49
|
|
|
49
50
|
assert (
|
|
@@ -52,34 +53,39 @@ def _get_names(set_code: str) -> tuple[str, ...]:
|
|
|
52
53
|
return names
|
|
53
54
|
|
|
54
55
|
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
ColType.AGG,
|
|
59
|
-
ColType.CARD_SUM,
|
|
60
|
-
):
|
|
61
|
-
return set()
|
|
62
|
-
if spec.col_type == ColType.CARD_ATTR:
|
|
63
|
-
return {View.CARD}
|
|
64
|
-
if spec.views is not None:
|
|
65
|
-
return set(spec.views)
|
|
66
|
-
assert (
|
|
67
|
-
spec.dependencies is not None
|
|
68
|
-
), f"Col {spec.name} should have dependencies"
|
|
69
|
-
|
|
70
|
-
views = functools.reduce(
|
|
71
|
-
lambda prev, curr: prev.intersection(curr),
|
|
72
|
-
[get_views(col_spec_map[dep]) for dep in spec.dependencies],
|
|
73
|
-
)
|
|
56
|
+
def _get_card_context(set_code: str, col_spec_map: dict[str, ColumnSpec]) -> dict[str, dict[str, Any]]:
|
|
57
|
+
card_attr_specs = {col:spec for col, spec in col_spec_map.items() if spec.col_type == ColType.CARD_ATTR or spec.name == ColName.NAME}
|
|
58
|
+
col_def_map = _hydrate_col_defs(set_code, card_attr_specs, card_only=True)
|
|
74
59
|
|
|
75
|
-
|
|
60
|
+
columns = list(col_def_map.keys())
|
|
61
|
+
|
|
62
|
+
fp = data_file_path(set_code, View.CARD)
|
|
63
|
+
card_df = pl.read_parquet(fp)
|
|
64
|
+
select_rows = _view_select(
|
|
65
|
+
card_df, frozenset(columns), col_def_map, is_agg_view=False
|
|
66
|
+
).to_dicts()
|
|
67
|
+
|
|
68
|
+
card_context = {row[ColName.NAME]: row for row in select_rows}
|
|
69
|
+
|
|
70
|
+
return card_context
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _determine_expression(spec: ColumnSpec, names: list[str], card_context: dict[str, dict]) -> pl.Expr | tuple[pl.Expr, ...]:
|
|
74
|
+
def seed_params(expr):
|
|
75
|
+
params = {}
|
|
76
|
+
|
|
77
|
+
sig_params = signature(expr).parameters
|
|
78
|
+
if 'names' in sig_params:
|
|
79
|
+
params['names'] = names
|
|
80
|
+
if 'card_context' in sig_params:
|
|
81
|
+
params['card_context'] = card_context
|
|
82
|
+
return params
|
|
83
|
+
|
|
84
|
+
if spec.col_type == ColType.NAME_SUM:
|
|
85
|
+
if spec.expr is not None:
|
|
86
|
+
assert isinstance(spec.expr, Callable), f"NAME_SUM column {spec.name} must have a callable `expr` accepting a `name` argument"
|
|
87
|
+
unnamed_exprs = [spec.expr(**{'name': name, **seed_params(spec.expr)}) for name in names]
|
|
76
88
|
|
|
77
|
-
names = _get_names(set_code)
|
|
78
|
-
assert len(names) > 0, "there should be names"
|
|
79
|
-
hydrated = {}
|
|
80
|
-
for key, spec in col_spec_map.items():
|
|
81
|
-
if spec.col_type == ColType.NAME_SUM and spec.exprMap is not None:
|
|
82
|
-
unnamed_exprs = map(spec.exprMap, names)
|
|
83
89
|
expr = tuple(
|
|
84
90
|
map(
|
|
85
91
|
lambda ex, name: ex.alias(f"{spec.name}_{name}"),
|
|
@@ -87,14 +93,76 @@ def _hydrate_col_defs(set_code: str, col_spec_map: dict[str, ColumnSpec]):
|
|
|
87
93
|
names,
|
|
88
94
|
)
|
|
89
95
|
)
|
|
90
|
-
elif spec.expr is not None:
|
|
91
|
-
expr = spec.expr.alias(spec.name)
|
|
92
|
-
|
|
93
96
|
else:
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
expr = tuple(map(lambda name: pl.col(f"{spec.name}_{name}"), names))
|
|
98
|
+
|
|
99
|
+
elif spec.expr is not None:
|
|
100
|
+
if isinstance(spec.expr, Callable):
|
|
101
|
+
params = seed_params(spec.expr)
|
|
102
|
+
if spec.col_type == ColType.PICK_SUM and 'name' in signature(spec.expr).parameters:
|
|
103
|
+
expr = pl.lit(None)
|
|
104
|
+
for name in names:
|
|
105
|
+
name_params = {'name': name, **params}
|
|
106
|
+
expr = pl.when(pl.col(ColName.PICK) == name).then(spec.expr(**name_params)).otherwise(expr)
|
|
96
107
|
else:
|
|
97
|
-
expr =
|
|
108
|
+
expr = spec.expr(**params)
|
|
109
|
+
else:
|
|
110
|
+
expr = spec.expr
|
|
111
|
+
expr = expr.alias(spec.name)
|
|
112
|
+
else:
|
|
113
|
+
expr = pl.col(spec.name)
|
|
114
|
+
|
|
115
|
+
return expr
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _infer_dependencies(name: str, expr: pl.Expr | tuple[pl.Expr,...], col_spec_map: dict[str, ColumnSpec], names: list[str]) -> set[str]:
|
|
119
|
+
dependencies = set()
|
|
120
|
+
tricky_ones = set()
|
|
121
|
+
|
|
122
|
+
if isinstance(expr, pl.Expr):
|
|
123
|
+
dep_cols = [c for c in expr.meta.root_names() if c != name]
|
|
124
|
+
for dep_col in dep_cols:
|
|
125
|
+
if dep_col in col_spec_map.keys():
|
|
126
|
+
dependencies.add(dep_col)
|
|
127
|
+
else:
|
|
128
|
+
tricky_ones.add(dep_col)
|
|
129
|
+
else:
|
|
130
|
+
for idx, exp in enumerate(expr):
|
|
131
|
+
pattern = f"_{names[idx]}$"
|
|
132
|
+
dep_cols = [c for c in exp.meta.root_names() if c != name]
|
|
133
|
+
for dep_col in dep_cols:
|
|
134
|
+
if dep_col in col_spec_map.keys():
|
|
135
|
+
dependencies.add(dep_col)
|
|
136
|
+
elif len(split := re.split(pattern, dep_col)) == 2 and split[0] in col_spec_map:
|
|
137
|
+
dependencies.add(split[0])
|
|
138
|
+
else:
|
|
139
|
+
tricky_ones.add(dep_col)
|
|
140
|
+
|
|
141
|
+
for item in tricky_ones:
|
|
142
|
+
found = False
|
|
143
|
+
for n in names:
|
|
144
|
+
pattern = f"_{n}$"
|
|
145
|
+
if not found and len(split := re.split(pattern, item)) == 2 and split[0] in col_spec_map:
|
|
146
|
+
dependencies.add(split[0])
|
|
147
|
+
found = True
|
|
148
|
+
assert found, f"Could not locate column spec for root col {item}"
|
|
149
|
+
|
|
150
|
+
return dependencies
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _hydrate_col_defs(set_code: str, col_spec_map: dict[str, ColumnSpec], card_only=False):
|
|
154
|
+
names = _get_names(set_code)
|
|
155
|
+
|
|
156
|
+
if card_only:
|
|
157
|
+
card_context = {}
|
|
158
|
+
else:
|
|
159
|
+
card_context = _get_card_context(set_code, col_spec_map)
|
|
160
|
+
|
|
161
|
+
assert len(names) > 0, "there should be names"
|
|
162
|
+
hydrated = {}
|
|
163
|
+
for key, spec in col_spec_map.items():
|
|
164
|
+
expr = _determine_expression(spec, names, card_context)
|
|
165
|
+
dependencies = _infer_dependencies(key, expr, col_spec_map, names)
|
|
98
166
|
|
|
99
167
|
try:
|
|
100
168
|
sig_expr = expr if isinstance(expr, pl.Expr) else expr[0]
|
|
@@ -107,14 +175,11 @@ def _hydrate_col_defs(set_code: str, col_spec_map: dict[str, ColumnSpec]):
|
|
|
107
175
|
else:
|
|
108
176
|
expr_sig = str(datetime.datetime.now)
|
|
109
177
|
|
|
110
|
-
dependencies = tuple(spec.dependencies or ())
|
|
111
|
-
views = get_views(spec)
|
|
112
178
|
signature = str(
|
|
113
179
|
(
|
|
114
180
|
spec.name,
|
|
115
181
|
spec.col_type.value,
|
|
116
182
|
expr_sig,
|
|
117
|
-
tuple(view.value for view in views),
|
|
118
183
|
dependencies,
|
|
119
184
|
)
|
|
120
185
|
)
|
|
@@ -122,7 +187,7 @@ def _hydrate_col_defs(set_code: str, col_spec_map: dict[str, ColumnSpec]):
|
|
|
122
187
|
cdef = ColumnDefinition(
|
|
123
188
|
name=spec.name,
|
|
124
189
|
col_type=spec.col_type,
|
|
125
|
-
views=views,
|
|
190
|
+
views=set(spec.views or set()),
|
|
126
191
|
expr=expr,
|
|
127
192
|
dependencies=dependencies,
|
|
128
193
|
signature=signature,
|
|
@@ -136,18 +201,13 @@ def _view_select(
|
|
|
136
201
|
view_cols: frozenset[str],
|
|
137
202
|
col_def_map: dict[str, ColumnDefinition],
|
|
138
203
|
is_agg_view: bool,
|
|
139
|
-
is_card_sum: bool = False,
|
|
140
204
|
) -> DF:
|
|
141
205
|
base_cols = frozenset()
|
|
142
206
|
cdefs = [col_def_map[c] for c in view_cols]
|
|
143
207
|
select = []
|
|
144
208
|
for cdef in cdefs:
|
|
145
209
|
if is_agg_view:
|
|
146
|
-
if
|
|
147
|
-
cdef.col_type == ColType.AGG
|
|
148
|
-
or cdef.col_type == ColType.CARD_SUM
|
|
149
|
-
and is_card_sum
|
|
150
|
-
):
|
|
210
|
+
if cdef.col_type == ColType.AGG:
|
|
151
211
|
base_cols = base_cols.union(cdef.dependencies)
|
|
152
212
|
select.append(cdef.expr)
|
|
153
213
|
else:
|
|
@@ -164,7 +224,7 @@ def _view_select(
|
|
|
164
224
|
select.append(cdef.expr)
|
|
165
225
|
|
|
166
226
|
if base_cols != view_cols:
|
|
167
|
-
df = _view_select(df, base_cols, col_def_map, is_agg_view
|
|
227
|
+
df = _view_select(df, base_cols, col_def_map, is_agg_view)
|
|
168
228
|
|
|
169
229
|
return df.select(select)
|
|
170
230
|
|
|
@@ -274,28 +334,6 @@ def _base_agg_df(
|
|
|
274
334
|
)
|
|
275
335
|
|
|
276
336
|
|
|
277
|
-
def card_df(
|
|
278
|
-
set_code: str,
|
|
279
|
-
extensions: list[ColumnSpec] | None = None,
|
|
280
|
-
):
|
|
281
|
-
col_spec_map = dict(spells.columns.col_spec_map)
|
|
282
|
-
if extensions is not None:
|
|
283
|
-
for spec in extensions:
|
|
284
|
-
col_spec_map[spec.name] = spec
|
|
285
|
-
|
|
286
|
-
col_def_map = _hydrate_col_defs(set_code, col_spec_map)
|
|
287
|
-
|
|
288
|
-
columns = [ColName.NAME] + [
|
|
289
|
-
c for c, cdef in col_def_map.items() if cdef.col_type == ColType.CARD_ATTR
|
|
290
|
-
]
|
|
291
|
-
fp = data_file_path(set_code, View.CARD)
|
|
292
|
-
card_df = pl.read_parquet(fp)
|
|
293
|
-
select_df = _view_select(
|
|
294
|
-
card_df, frozenset(columns), col_def_map, is_agg_view=False
|
|
295
|
-
)
|
|
296
|
-
return select_df.select(columns)
|
|
297
|
-
|
|
298
|
-
|
|
299
337
|
def summon(
|
|
300
338
|
set_code: str,
|
|
301
339
|
columns: list[str] | None = None,
|
|
@@ -337,12 +375,6 @@ def summon(
|
|
|
337
375
|
select_df = _view_select(card_df, card_cols, m.col_def_map, is_agg_view=False)
|
|
338
376
|
agg_df = agg_df.join(select_df, on="name", how="outer", coalesce=True)
|
|
339
377
|
|
|
340
|
-
if m.card_sum:
|
|
341
|
-
card_sum_df = _view_select(
|
|
342
|
-
agg_df, m.card_sum, m.col_def_map, is_agg_view=True, is_card_sum=True
|
|
343
|
-
)
|
|
344
|
-
agg_df = pl.concat([agg_df, card_sum_df], how="horizontal")
|
|
345
|
-
|
|
346
378
|
if ColName.NAME not in m.group_by:
|
|
347
379
|
agg_df = agg_df.group_by(m.group_by).sum()
|
|
348
380
|
|
spells/enums.py
CHANGED
|
@@ -19,7 +19,6 @@ class ColType(StrEnum):
|
|
|
19
19
|
NAME_SUM = "name_sum"
|
|
20
20
|
AGG = "agg"
|
|
21
21
|
CARD_ATTR = "card_attr"
|
|
22
|
-
CARD_SUM = "card_sum"
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
class ColName(StrEnum):
|
|
@@ -117,6 +116,8 @@ class ColName(StrEnum):
|
|
|
117
116
|
SUBTYPE = "subtype"
|
|
118
117
|
MANA_VALUE = "mana_value"
|
|
119
118
|
DECK_MANA_VALUE = "deck_mana_value"
|
|
119
|
+
DECK_LANDS = "deck_lands"
|
|
120
|
+
DECK_SPELLS = "deck_spells"
|
|
120
121
|
MANA_COST = "mana_cost"
|
|
121
122
|
POWER = "power"
|
|
122
123
|
TOUGHNESS = "toughness"
|
|
@@ -157,3 +158,5 @@ class ColName(StrEnum):
|
|
|
157
158
|
GIH_WR_STDEV = "gh_wr_stdev"
|
|
158
159
|
GIH_WR_Z = "gih_wr_z"
|
|
159
160
|
DECK_MANA_VALUE_AVG = "deck_mana_value_avg"
|
|
161
|
+
DECK_LANDS_AVG = "deck_lands_avg"
|
|
162
|
+
DECK_SPELLS_AVG = "deck_spells_avg"
|
spells/manifest.py
CHANGED
|
@@ -14,7 +14,6 @@ class Manifest:
|
|
|
14
14
|
view_cols: dict[View, frozenset[str]]
|
|
15
15
|
group_by: tuple[str, ...]
|
|
16
16
|
filter: spells.filter.Filter | None
|
|
17
|
-
card_sum: frozenset[str]
|
|
18
17
|
|
|
19
18
|
def __post_init__(self):
|
|
20
19
|
# No name filter check
|
|
@@ -40,21 +39,13 @@ class Manifest:
|
|
|
40
39
|
), f"Invalid groupby {col}!"
|
|
41
40
|
|
|
42
41
|
for view, cols_for_view in self.view_cols.items():
|
|
43
|
-
# cols_for_view are actually in view check
|
|
44
42
|
for col in cols_for_view:
|
|
45
|
-
assert (
|
|
46
|
-
view in self.col_def_map[col].views
|
|
47
|
-
), f"View cols generated incorrectly, {col} not in view {view}"
|
|
48
43
|
# game sum cols on in game, and no NAME groupby
|
|
49
44
|
assert self.col_def_map[col].col_type != ColType.GAME_SUM or (
|
|
50
45
|
view == View.GAME and ColName.NAME not in self.base_view_group_by
|
|
51
46
|
), f"Invalid manifest for GAME_SUM column {col}"
|
|
52
47
|
if view != View.CARD:
|
|
53
48
|
for col in self.base_view_group_by:
|
|
54
|
-
# base_view_groupbys in view check
|
|
55
|
-
assert (
|
|
56
|
-
col == ColName.NAME or view in self.col_def_map[col].views
|
|
57
|
-
), f"Groupby {col} not in view {view}!"
|
|
58
49
|
# base_view_groupbys in view_cols for view
|
|
59
50
|
assert (
|
|
60
51
|
col == ColName.NAME or col in cols_for_view
|
|
@@ -95,7 +86,7 @@ class Manifest:
|
|
|
95
86
|
def _resolve_view_cols(
|
|
96
87
|
col_set: frozenset[str],
|
|
97
88
|
col_def_map: dict[str, ColumnDefinition],
|
|
98
|
-
) ->
|
|
89
|
+
) -> dict[View, frozenset[str]]:
|
|
99
90
|
"""
|
|
100
91
|
For each view ('game', 'draft', and 'card'), return the columns
|
|
101
92
|
that must be present at the aggregation step. 'name' need not be
|
|
@@ -104,7 +95,6 @@ def _resolve_view_cols(
|
|
|
104
95
|
MAX_DEPTH = 1000
|
|
105
96
|
unresolved_cols = col_set
|
|
106
97
|
view_resolution = {}
|
|
107
|
-
card_sum = frozenset()
|
|
108
98
|
|
|
109
99
|
iter_num = 0
|
|
110
100
|
while unresolved_cols and iter_num < MAX_DEPTH:
|
|
@@ -116,9 +106,9 @@ def _resolve_view_cols(
|
|
|
116
106
|
view_resolution[View.DRAFT] = view_resolution.get(
|
|
117
107
|
View.DRAFT, frozenset()
|
|
118
108
|
).union({ColName.PICK})
|
|
119
|
-
if cdef.col_type == ColType.
|
|
120
|
-
|
|
121
|
-
|
|
109
|
+
if cdef.col_type == ColType.CARD_ATTR:
|
|
110
|
+
view_resolution[View.CARD] = view_resolution.get(View.CARD, frozenset()).union({col})
|
|
111
|
+
elif cdef.views:
|
|
122
112
|
for view in cdef.views:
|
|
123
113
|
view_resolution[view] = view_resolution.get(
|
|
124
114
|
view, frozenset()
|
|
@@ -128,14 +118,38 @@ def _resolve_view_cols(
|
|
|
128
118
|
raise ValueError(
|
|
129
119
|
f"Invalid column def: {col} has neither views nor dependencies!"
|
|
130
120
|
)
|
|
131
|
-
|
|
132
|
-
|
|
121
|
+
if cdef.col_type != ColType.AGG:
|
|
122
|
+
fully_resolved = True
|
|
123
|
+
col_views = frozenset({View.GAME, View.DRAFT, View.CARD})
|
|
124
|
+
for dep in cdef.dependencies:
|
|
125
|
+
dep_views = frozenset()
|
|
126
|
+
for view, view_cols in view_resolution.items():
|
|
127
|
+
if dep in view_cols:
|
|
128
|
+
dep_views = dep_views.union({view})
|
|
129
|
+
if not dep_views:
|
|
130
|
+
fully_resolved = False
|
|
131
|
+
next_cols = next_cols.union({dep})
|
|
132
|
+
else:
|
|
133
|
+
col_views = col_views.intersection(dep_views)
|
|
134
|
+
if fully_resolved:
|
|
135
|
+
assert len(col_views), f"Column {col} can't be defined in any views!"
|
|
136
|
+
for view in col_views:
|
|
137
|
+
if view not in view_resolution:
|
|
138
|
+
print(cdef)
|
|
139
|
+
assert False, f"Something went wrong with col {col}"
|
|
140
|
+
|
|
141
|
+
view_resolution[view] = view_resolution[view].union({col})
|
|
142
|
+
else:
|
|
143
|
+
next_cols = next_cols.union({col})
|
|
144
|
+
else:
|
|
145
|
+
for dep in cdef.dependencies:
|
|
146
|
+
next_cols = next_cols.union({dep})
|
|
133
147
|
unresolved_cols = next_cols
|
|
134
148
|
|
|
135
149
|
if iter_num >= MAX_DEPTH:
|
|
136
150
|
raise ValueError("broken dependency chain in column spec, loop probable")
|
|
137
151
|
|
|
138
|
-
return view_resolution
|
|
152
|
+
return view_resolution
|
|
139
153
|
|
|
140
154
|
|
|
141
155
|
def create(
|
|
@@ -148,7 +162,7 @@ def create(
|
|
|
148
162
|
if columns is None:
|
|
149
163
|
cols = tuple(spells.columns.default_columns)
|
|
150
164
|
if ColName.NAME not in gbs:
|
|
151
|
-
cols = tuple(c for c in cols if c
|
|
165
|
+
cols = tuple(c for c in cols if col_def_map[c].col_type != ColType.CARD_ATTR)
|
|
152
166
|
else:
|
|
153
167
|
cols = tuple(columns)
|
|
154
168
|
|
|
@@ -159,12 +173,8 @@ def create(
|
|
|
159
173
|
if m_filter is not None:
|
|
160
174
|
col_set = col_set.union(m_filter.lhs)
|
|
161
175
|
|
|
162
|
-
view_cols, card_sum = _resolve_view_cols(col_set, col_def_map)
|
|
163
176
|
base_view_group_by = frozenset()
|
|
164
177
|
|
|
165
|
-
if card_sum:
|
|
166
|
-
base_view_group_by = base_view_group_by.union({ColName.NAME})
|
|
167
|
-
|
|
168
178
|
for col in gbs:
|
|
169
179
|
cdef = col_def_map[col]
|
|
170
180
|
if cdef.col_type == ColType.GROUP_BY:
|
|
@@ -172,14 +182,23 @@ def create(
|
|
|
172
182
|
elif cdef.col_type == ColType.CARD_ATTR:
|
|
173
183
|
base_view_group_by = base_view_group_by.union({ColName.NAME})
|
|
174
184
|
|
|
185
|
+
view_cols = _resolve_view_cols(col_set, col_def_map)
|
|
186
|
+
|
|
175
187
|
needed_views = frozenset()
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if col_def_map[col].views == {view}: # only found in this view
|
|
179
|
-
needed_views = needed_views.union({view})
|
|
188
|
+
if View.CARD in view_cols:
|
|
189
|
+
needed_views = needed_views.union({View.CARD})
|
|
180
190
|
|
|
181
|
-
|
|
182
|
-
|
|
191
|
+
draft_view_cols = view_cols.get(View.DRAFT, frozenset())
|
|
192
|
+
game_view_cols = view_cols.get(View.GAME, frozenset())
|
|
193
|
+
|
|
194
|
+
base_cols = draft_view_cols.union(game_view_cols)
|
|
195
|
+
|
|
196
|
+
if base_cols == draft_view_cols:
|
|
197
|
+
needed_views = needed_views.union({View.DRAFT})
|
|
198
|
+
elif base_cols == game_view_cols:
|
|
199
|
+
needed_views = needed_views.union({View.GAME})
|
|
200
|
+
else:
|
|
201
|
+
needed_views = needed_views.union({View.GAME, View.DRAFT})
|
|
183
202
|
|
|
184
203
|
view_cols = {v: view_cols[v] for v in needed_views}
|
|
185
204
|
|
|
@@ -190,5 +209,4 @@ def create(
|
|
|
190
209
|
view_cols=view_cols,
|
|
191
210
|
group_by=gbs,
|
|
192
211
|
filter=m_filter,
|
|
193
|
-
card_sum=card_sum,
|
|
194
212
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: spells-mtg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: analaysis of 17Lands.com public datasets
|
|
5
5
|
Author-Email: Joel Barnes <oelarnes@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -74,7 +74,7 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
74
74
|
- Supports calculating the standard aggregations and measures out of the box with no arguments (ALSA, GIH WR, etc)
|
|
75
75
|
- Caches aggregate DataFrames in the local file system automatically for instantaneous reproduction of previous analysis
|
|
76
76
|
- Manages grouping and filtering by built-in and custom columns at the row level
|
|
77
|
-
- Provides
|
|
77
|
+
- Provides 121 explicitly specified, enumerated, documented column definitions
|
|
78
78
|
- Supports "Deck Color Data" aggregations with built-in column definitions.
|
|
79
79
|
- Provides a CLI tool `spells [add|refresh|clean|remove|info] [SET]` to download and manage external files
|
|
80
80
|
- Downloads and manages public datasets from 17Lands
|
|
@@ -112,24 +112,24 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
112
112
|
```
|
|
113
113
|
- `group_by` specifies the grouping by one or more columns. By default, group by card names, but optionally group by any of a large set of fundamental and derived columns, including card attributes and your own custom extension.
|
|
114
114
|
```python
|
|
115
|
-
>>>
|
|
116
|
-
shape: (10,
|
|
117
|
-
|
|
118
|
-
│ main_colors ┆ num_won ┆ num_games ┆ game_wr │
|
|
119
|
-
│ --- ┆ --- ┆ --- ┆ --- │
|
|
120
|
-
│ str ┆ u32 ┆ u32 ┆ f64 │
|
|
121
|
-
|
|
122
|
-
│ BG ┆ 85022 ┆ 152863 ┆ 0.556197 │
|
|
123
|
-
│ BR ┆ 45900 ┆ 81966 ┆ 0.559988 │
|
|
124
|
-
│ RG ┆ 34641 ┆ 64428 ┆ 0.53767 │
|
|
125
|
-
│ UB ┆ 30922 ┆ 57698 ┆ 0.535928 │
|
|
126
|
-
│ UG ┆ 59879 ┆ 109145 ┆ 0.548619 │
|
|
127
|
-
│ UR ┆ 19638 ┆ 38679 ┆ 0.507717 │
|
|
128
|
-
│ WB ┆ 59480 ┆ 107443 ┆ 0.553596 │
|
|
129
|
-
│ WG ┆ 76134 ┆ 136832 ┆ 0.556405 │
|
|
130
|
-
│ WR ┆ 49712 ┆ 91224 ┆ 0.544944 │
|
|
131
|
-
│ WU ┆ 16483 ┆ 31450 ┆ 0.524102 │
|
|
132
|
-
|
|
115
|
+
>>> summon('BLB', columns=["num_won", "num_games", "game_wr", "deck_mana_value_avg"], group_by=["main_colors"], filter_spec={"num_colors": 2})
|
|
116
|
+
shape: (10, 5)
|
|
117
|
+
┌─────────────┬─────────┬───────────┬──────────┬─────────────────────┐
|
|
118
|
+
│ main_colors ┆ num_won ┆ num_games ┆ game_wr ┆ deck_mana_value_avg │
|
|
119
|
+
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
|
|
120
|
+
│ str ┆ u32 ┆ u32 ┆ f64 ┆ f64 │
|
|
121
|
+
╞═════════════╪═════════╪═══════════╪══════════╪═════════════════════╡
|
|
122
|
+
│ BG ┆ 85022 ┆ 152863 ┆ 0.556197 ┆ 2.862305 │
|
|
123
|
+
│ BR ┆ 45900 ┆ 81966 ┆ 0.559988 ┆ 2.76198 │
|
|
124
|
+
│ RG ┆ 34641 ┆ 64428 ┆ 0.53767 ┆ 2.852182 │
|
|
125
|
+
│ UB ┆ 30922 ┆ 57698 ┆ 0.535928 ┆ 3.10409 │
|
|
126
|
+
│ UG ┆ 59879 ┆ 109145 ┆ 0.548619 ┆ 2.861026 │
|
|
127
|
+
│ UR ┆ 19638 ┆ 38679 ┆ 0.507717 ┆ 2.908215 │
|
|
128
|
+
│ WB ┆ 59480 ┆ 107443 ┆ 0.553596 ┆ 2.9217 │
|
|
129
|
+
│ WG ┆ 76134 ┆ 136832 ┆ 0.556405 ┆ 2.721064 │
|
|
130
|
+
│ WR ┆ 49712 ┆ 91224 ┆ 0.544944 ┆ 2.5222 │
|
|
131
|
+
│ WU ┆ 16483 ┆ 31450 ┆ 0.524102 ┆ 2.930967 │
|
|
132
|
+
└─────────────┴─────────┴───────────┴──────────┴─────────────────────┘
|
|
133
133
|
```
|
|
134
134
|
- `filter_spec` specifies a row-level filter for the dataset, using an intuitive custom query formulation
|
|
135
135
|
```python
|
|
@@ -156,8 +156,7 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
156
156
|
... name='deq_base',
|
|
157
157
|
... col_type=ColType.AGG,
|
|
158
158
|
... expr=(pl.col('gp_wr_excess') + 0.03 * (1 - pl.col('ata')/14).pow(2)) * pl.col('pct_gp'),
|
|
159
|
-
...
|
|
160
|
-
...)
|
|
159
|
+
... )
|
|
161
160
|
>>> spells.summon('DSK', columns=['deq_base', 'color', 'rarity'], filter_spec={'player_cohort': 'Top'}, extensions=[ext])
|
|
162
161
|
... .filter(pl.col('deq_base').is_finite())
|
|
163
162
|
... .filter(pl.col('rarity').is_in(['common', 'uncommon'])
|
|
@@ -278,13 +277,13 @@ summon(
|
|
|
278
277
|
|
|
279
278
|
#### parameters
|
|
280
279
|
|
|
281
|
-
- columns: a list of string or `ColName` values to select as non-grouped columns. Valid `ColTypes` are `PICK_SUM`, `NAME_SUM`, `GAME_SUM`, `CARD_ATTR`,
|
|
280
|
+
- columns: a list of string or `ColName` values to select as non-grouped columns. Valid `ColTypes` are `PICK_SUM`, `NAME_SUM`, `GAME_SUM`, `CARD_ATTR`, and `AGG`. Min/Max/Unique
|
|
282
281
|
aggregations of non-numeric (or numeric) data types are not supported. If `None`, use a set of columns modeled on the commonly used values on 17Lands.com/card_data.
|
|
283
282
|
|
|
284
283
|
- group_by: a list of string or `ColName` values to display as grouped columns. Valid `ColTypes` are `GROUP_BY` and `CARD_ATTR`. By default, group by "name" (card name).
|
|
285
284
|
|
|
286
285
|
- filter_spec: a dictionary specifying a filter, using a small number of paradigms. Columns used must be in each base view ("draft" and "game") that the `columns` and `group_by` columns depend on, so
|
|
287
|
-
`AGG
|
|
286
|
+
`AGG` and `CARD_ATTR` columns are not valid. Functions of card attributes in the base views can be filtered on, see the documentation for `expr` for details. `NAME_SUM` columns are also not supported. Derived columns are supported. No filter is applied by default. Yes, I should rewrite it to use the mongo query language. The specification is best understood with examples:
|
|
288
287
|
|
|
289
288
|
- `{'player_cohort': 'Top'}` "player_cohort" value equals "Top".
|
|
290
289
|
- `{'lhs': 'player_cohort', 'op': 'in', 'rhs': ['Top', 'Middle']}` "player_cohort" value is either "Top" or "Middle". Supported values for `op` are `<`, `<=`, `>`, `>=`, `!=`, `=`, `in` and `nin`.
|
|
@@ -297,10 +296,10 @@ aggregations of non-numeric (or numeric) data types are not supported. If `None`
|
|
|
297
296
|
### Enums
|
|
298
297
|
|
|
299
298
|
```python
|
|
300
|
-
from spells.enums import ColName, ColType
|
|
299
|
+
from spells.enums import ColName, ColType
|
|
301
300
|
```
|
|
302
301
|
|
|
303
|
-
Recommended to import `ColName` for any usage of `summon`, and to import `ColType` when defining custom extensions.
|
|
302
|
+
Recommended to import `ColName` for any usage of `summon`, and to import `ColType` when defining custom extensions.
|
|
304
303
|
|
|
305
304
|
### ColumnSpec
|
|
306
305
|
|
|
@@ -310,11 +309,8 @@ from spells.columns import ColumnSpec
|
|
|
310
309
|
ColumnSpec(
|
|
311
310
|
name: str,
|
|
312
311
|
col_type: ColType,
|
|
313
|
-
expr: pl.Expr | None = None,
|
|
314
|
-
exprMap: Callable[[str], pl.Expr] | None = None
|
|
315
|
-
dependencies: list[str] | None = None
|
|
312
|
+
expr: pl.Expr | Callable[..., pl.Expr] | None = None,
|
|
316
313
|
version: str | None = None
|
|
317
|
-
views: list[View] | None = None,
|
|
318
314
|
)
|
|
319
315
|
```
|
|
320
316
|
|
|
@@ -324,19 +320,19 @@ Used to define extensions in `summon`
|
|
|
324
320
|
|
|
325
321
|
- `name`: any string, including existing columns, although this is very likely to break dependent columns, so don't do it. For `NAME_SUM` columns, the name is the prefix without the underscore, e.g. "drawn".
|
|
326
322
|
|
|
327
|
-
- `col_type`: one of the `ColType` enum values, `FILTER_ONLY`, `GROUP_BY`, `PICK_SUM`, `NAME_SUM`, `GAME_SUM`, `CARD_ATTR`,
|
|
328
|
-
|
|
329
|
-
|
|
323
|
+
- `col_type`: one of the `ColType` enum values, `FILTER_ONLY`, `GROUP_BY`, `PICK_SUM`, `NAME_SUM`, `GAME_SUM`, `CARD_ATTR`, and `AGG`. See documentation for `summon` for usage. All columns except `CARD_ATTR`
|
|
324
|
+
and `AGG` must be derivable at the individual row level on one or both base views. `CARD_ATTR` must be derivable at the individual row level from the card file. `AGG` can depend on any column present after
|
|
325
|
+
summing over groups, and can include polars Expression aggregations. Arbitrarily long chains of aggregate dependencies are supported.
|
|
330
326
|
|
|
331
|
-
- `
|
|
332
|
-
|
|
333
|
-
- `
|
|
327
|
+
- `expr`: A polars expression or function returning a polars expression giving the derivation of the column value at the first level where it is defined.
|
|
328
|
+
- For `NAME_SUM` columns, `expr` must be a function of `name` which will result in a list of expressions mapped over all card names.
|
|
329
|
+
- `PICK_SUM` columns can also be functions on `name`, in which case the value will be a function of the value of the `PICK` field.
|
|
330
|
+
- `AGG` columns that depend on `NAME_SUM` columns reference the prefix (`cdef.name`) only, since the unpivot has occured prior to selection.
|
|
331
|
+
- The possible arguments to `expr`, in addition to `name` when appropriate, include the full `names` array as well as a dictionary called `card_context` which contains card dict objects with all `CARD_ATTR` values, including custom extensions. See example notebooks for more details.
|
|
334
332
|
|
|
335
333
|
- `version`: When defining a column using a python function, as opposed to Polars expressions, add a unique version number so that the unique hashed signature of the column specification can be derived
|
|
336
334
|
for caching purposes, since Polars cannot generate a serialization natively. When changing the definition, be sure to increment the version value. Otherwise you do not need to use this parameter.
|
|
337
335
|
|
|
338
|
-
- `views`: Not needed for custom columns.
|
|
339
|
-
|
|
340
336
|
### Columns
|
|
341
337
|
|
|
342
338
|
A table of all included columns. Columns can be referenced by enum or by string value in arguments and filter specs. The string value is always the lowercase version of the enum attribute.
|
|
@@ -424,6 +420,9 @@ A table of all included columns. Columns can be referenced by enum or by string
|
|
|
424
420
|
| `CARD_TYPE` | `"card_type"` | `CARD` | `CARD_ATTR` | | String |
|
|
425
421
|
| `SUBTYPE` | `"subtype"` | `CARD` | `CARD_ATTR` | | String |
|
|
426
422
|
| `MANA_VALUE` | `"mana_value"` | `CARD` | `CARD_ATTR` | | Float |
|
|
423
|
+
| `DECK_MANA_VALUE` | `"deck_mana_value"` | | `NAME_SUM` | `DECK` * `MANA_VALUE` | Float |
|
|
424
|
+
| `DECK_LANDS` | `"deck_lands"` | | `NAME_SUM` | Number of lands in deck | Float |
|
|
425
|
+
| `DECK_SPELLS` | `"deck_spells"` | | `NAME_SUM` | Number of spells in deck | Float |
|
|
427
426
|
| `MANA_COST` | `"mana_cost"` | `CARD` | `CARD_ATTR` | | String |
|
|
428
427
|
| `POWER` | `"power"` | `CARD` | `CARD_ATTR` | | Float |
|
|
429
428
|
| `TOUGHNESS` | `"toughness"` | `CARD` | `CARD_ATTR` | | Float |
|
|
@@ -462,6 +461,9 @@ A table of all included columns. Columns can be referenced by enum or by string
|
|
|
462
461
|
| `GIH_WR_VAR` | `"gih_wr_var"` | | `AGG` | Game-weighted Variance | Float |
|
|
463
462
|
| `GIH_WR_STDEV` | `"gh_wr_stdev"` | | `AGG` | Sqrt of `GIH_WR_VAR` | Float |
|
|
464
463
|
| `GIH_WR_Z` | `"gih_wr_z"` | | `AGG` |`GIH_WR_EXCESS` / `GIH_WR_STDEV` | Float |
|
|
464
|
+
| `DECK_MANA_VALUE_AVG` | `"deck_mana_value_avg"` | | `AGG` | `DECK_MANA_VALUE ` / `DECK_SPELLS` | Float |
|
|
465
|
+
| `DECK_LANDS_AVG` | `"deck_lands_avg"` | | `AGG` | `DECK_LANDS ` / `NUM_GAMES` | Float |
|
|
466
|
+
| `DECK_SPELLS_AVG` | `"deck_spells_avg"` | | `AGG` | `DECK_SPELLS ` / `NUM_GAMES` | Float |
|
|
465
467
|
|
|
466
468
|
# Roadmap to 1.0
|
|
467
469
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
spells/__init__.py,sha256=EcI7ijXYvPA8jj7wUZqs6CSWr__MD8AOXhkex-Hj37E,131
|
|
2
|
+
spells/cache.py,sha256=4v7h8D3TtaT0R_EdiRNhdcQrXzdH_CukezO6oAXvNEY,2956
|
|
3
|
+
spells/cards.py,sha256=EOXAB_F2yedjf6KquCERCIHl0TSIJIoOe1jv8g4JzOc,3601
|
|
4
|
+
spells/columns.py,sha256=gpgx8zZ4VmmcJXxMdyEKIOgJaBESKkbtpOm6bVNNopM,19132
|
|
5
|
+
spells/draft_data.py,sha256=bJw2ueJ1dNrunVY2GH6pgJCMPFCkydI37JExgpMS6vE,12559
|
|
6
|
+
spells/enums.py,sha256=4G-gi75v9bne4cMRgRwwmpWy0kW8NoTjkmGTxEwFDeo,4735
|
|
7
|
+
spells/external.py,sha256=qe6wOBDhPN4CZNQvYRq6G-OpIZcWTZzJjySgnf2Gu1o,10258
|
|
8
|
+
spells/filter.py,sha256=J-YTOOAzOQpvIX29tviYL04RVoOUlfsbjBXoQBDCEdQ,3380
|
|
9
|
+
spells/manifest.py,sha256=Dy0zskkSqPWb3WQqLgNk5pbaCGlEAn7JG9wRFOUA3Jc,8182
|
|
10
|
+
spells/schema.py,sha256=z8Qn2SiHG4T6YfPsz8xHLGMjU_Ofm76-Vrquh3b9B64,6422
|
|
11
|
+
spells_mtg-0.4.0.dist-info/METADATA,sha256=pEKOk-A27Tq8Fpbs9yPb11tT9av4f7-iOQSIGfWv9Mg,43908
|
|
12
|
+
spells_mtg-0.4.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
|
13
|
+
spells_mtg-0.4.0.dist-info/entry_points.txt,sha256=a9Y1omdl9MdnKuIj3aOodgrp-zZII6OCdvqwgP6BFvI,63
|
|
14
|
+
spells_mtg-0.4.0.dist-info/licenses/LICENSE,sha256=tS54XYbJSgmq5zuHhbsQGbNQLJPVgXqhF5nu2CSRMig,1068
|
|
15
|
+
spells_mtg-0.4.0.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
spells/__init__.py,sha256=QCPWQySUK2SZtCU-mSZLsn7vrNLJMDsRwil8gmAzmdk,151
|
|
2
|
-
spells/cache.py,sha256=4v7h8D3TtaT0R_EdiRNhdcQrXzdH_CukezO6oAXvNEY,2956
|
|
3
|
-
spells/cards.py,sha256=6seKpgI4TlJxI20bvcgt5VpxzsbCnnjWneDWpneJJ6Y,3497
|
|
4
|
-
spells/columns.py,sha256=_x7vVSh-55TJRjV7LQ29Kj_Ek__ef4pvxUsX57s9Iwo,22488
|
|
5
|
-
spells/draft_data.py,sha256=ABIQoUD8MKnc2goTUGY33dOxCpH8ydXXuQnbHMNULZs,10999
|
|
6
|
-
spells/enums.py,sha256=t-1PrBUqbxZRShDOgmTHkhiHIOIMHTbD7RUgKiQdx-c,4621
|
|
7
|
-
spells/external.py,sha256=qe6wOBDhPN4CZNQvYRq6G-OpIZcWTZzJjySgnf2Gu1o,10258
|
|
8
|
-
spells/filter.py,sha256=J-YTOOAzOQpvIX29tviYL04RVoOUlfsbjBXoQBDCEdQ,3380
|
|
9
|
-
spells/manifest.py,sha256=oGkWuBYquJYmtaIp2O-x5nFKD5TkU5lzS-vera69jyU,7244
|
|
10
|
-
spells/schema.py,sha256=z8Qn2SiHG4T6YfPsz8xHLGMjU_Ofm76-Vrquh3b9B64,6422
|
|
11
|
-
spells_mtg-0.3.0.dist-info/METADATA,sha256=hhHnMORK-fcalrC-Y416Eyfx-ZBaldqZYaXKpMSh7a8,42882
|
|
12
|
-
spells_mtg-0.3.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
|
13
|
-
spells_mtg-0.3.0.dist-info/entry_points.txt,sha256=a9Y1omdl9MdnKuIj3aOodgrp-zZII6OCdvqwgP6BFvI,63
|
|
14
|
-
spells_mtg-0.3.0.dist-info/licenses/LICENSE,sha256=tS54XYbJSgmq5zuHhbsQGbNQLJPVgXqhF5nu2CSRMig,1068
|
|
15
|
-
spells_mtg-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|