spells-mtg 0.1.0__py3-none-any.whl → 0.2.2__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 +9 -0
- spells/columns.py +44 -86
- spells/draft_data.py +43 -2
- spells/enums.py +2 -0
- spells/external.py +1 -1
- {spells_mtg-0.1.0.dist-info → spells_mtg-0.2.2.dist-info}/METADATA +31 -22
- spells_mtg-0.2.2.dist-info/RECORD +15 -0
- spells_mtg-0.1.0.dist-info/RECORD +0 -15
- {spells_mtg-0.1.0.dist-info → spells_mtg-0.2.2.dist-info}/WHEEL +0 -0
- {spells_mtg-0.1.0.dist-info → spells_mtg-0.2.2.dist-info}/entry_points.txt +0 -0
- {spells_mtg-0.1.0.dist-info → spells_mtg-0.2.2.dist-info}/licenses/LICENSE +0 -0
spells/__init__.py
CHANGED
spells/cards.py
CHANGED
|
@@ -21,6 +21,8 @@ class CardAttr(StrEnum):
|
|
|
21
21
|
TOUGHNESS = ColName.TOUGHNESS
|
|
22
22
|
IS_BONUS_SHEET = ColName.IS_BONUS_SHEET
|
|
23
23
|
IS_DFC = ColName.IS_DFC
|
|
24
|
+
ORACLE_TEXT = ColName.ORACLE_TEXT
|
|
25
|
+
CARD_JSON = ColName.CARD_JSON
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
MTG_JSON_TEMPLATE = "https://mtgjson.com/api/v5/{set_code}.json"
|
|
@@ -66,6 +68,10 @@ def _extract_value(set_code: str, name: str, card_dict: dict, field: CardAttr):
|
|
|
66
68
|
return card_dict.get("setCode", set_code) != set_code
|
|
67
69
|
case CardAttr.IS_DFC:
|
|
68
70
|
return len(card_dict.get("otherFaceIds", [])) > 0
|
|
71
|
+
case CardAttr.ORACLE_TEXT:
|
|
72
|
+
return card_dict.get("text", "")
|
|
73
|
+
case CardAttr.CARD_JSON:
|
|
74
|
+
return card_dict.get("json", "")
|
|
69
75
|
|
|
70
76
|
|
|
71
77
|
def card_df(draft_set_code: str, names: list[str]) -> pl.DataFrame:
|
|
@@ -81,6 +87,9 @@ def card_df(draft_set_code: str, names: list[str]) -> pl.DataFrame:
|
|
|
81
87
|
card_data = draft_set_json["data"]["cards"]
|
|
82
88
|
|
|
83
89
|
card_data.reverse() # prefer front face for split cards
|
|
90
|
+
for item in card_data:
|
|
91
|
+
item["json"] = json.dumps(item)
|
|
92
|
+
|
|
84
93
|
face_name_cards = [item for item in card_data if "faceName" in item]
|
|
85
94
|
card_data_map.update({item["faceName"]: item for item in face_name_cards})
|
|
86
95
|
card_data_map.update({item["name"]: item for item in card_data})
|
spells/columns.py
CHANGED
|
@@ -12,7 +12,7 @@ class ColumnSpec:
|
|
|
12
12
|
col_type: ColType
|
|
13
13
|
expr: pl.Expr | None = None
|
|
14
14
|
exprMap: Callable[[str], pl.Expr] | None = None
|
|
15
|
-
views:
|
|
15
|
+
views: list[View] | None = None
|
|
16
16
|
dependencies: list[str] | None = None
|
|
17
17
|
version: str | None = (
|
|
18
18
|
None # only needed for user-defined functions with python functions in expr
|
|
@@ -49,76 +49,70 @@ _column_specs = [
|
|
|
49
49
|
ColumnSpec(
|
|
50
50
|
name=ColName.NAME,
|
|
51
51
|
col_type=ColType.GROUP_BY,
|
|
52
|
-
views=(),
|
|
53
52
|
# handled by internals, derived from both 'pick' and "name mapped" columns
|
|
54
53
|
),
|
|
55
54
|
ColumnSpec(
|
|
56
55
|
name=ColName.EXPANSION,
|
|
57
56
|
col_type=ColType.GROUP_BY,
|
|
58
|
-
views=
|
|
57
|
+
views=[View.GAME, View.DRAFT],
|
|
59
58
|
),
|
|
60
59
|
ColumnSpec(
|
|
61
60
|
name=ColName.EVENT_TYPE,
|
|
62
61
|
col_type=ColType.GROUP_BY,
|
|
63
|
-
views=
|
|
62
|
+
views=[View.GAME, View.DRAFT],
|
|
64
63
|
),
|
|
65
64
|
ColumnSpec(
|
|
66
65
|
name=ColName.DRAFT_ID,
|
|
67
|
-
views=
|
|
66
|
+
views=[View.GAME, View.DRAFT],
|
|
68
67
|
col_type=ColType.FILTER_ONLY,
|
|
69
68
|
),
|
|
70
69
|
ColumnSpec(
|
|
71
70
|
name=ColName.DRAFT_TIME,
|
|
72
71
|
col_type=ColType.FILTER_ONLY,
|
|
73
|
-
views=
|
|
72
|
+
views=[View.GAME, View.DRAFT],
|
|
74
73
|
),
|
|
75
74
|
ColumnSpec(
|
|
76
75
|
name=ColName.DRAFT_DATE,
|
|
77
76
|
col_type=ColType.GROUP_BY,
|
|
78
|
-
views=(View.GAME, View.DRAFT),
|
|
79
77
|
expr=pl.col("draft_time").str.to_datetime("%Y-%m-%d %H:%M:%S").dt.date(),
|
|
80
78
|
dependencies=[ColName.DRAFT_TIME],
|
|
81
79
|
),
|
|
82
80
|
ColumnSpec(
|
|
83
81
|
name=ColName.DRAFT_DAY_OF_WEEK,
|
|
84
82
|
col_type=ColType.GROUP_BY,
|
|
85
|
-
views=(View.GAME, View.DRAFT),
|
|
86
83
|
expr=pl.col("draft_time").str.to_datetime("%Y-%m-%d %H:%M:%S").dt.weekday(),
|
|
87
84
|
dependencies=[ColName.DRAFT_TIME],
|
|
88
85
|
),
|
|
89
86
|
ColumnSpec(
|
|
90
87
|
name=ColName.DRAFT_HOUR,
|
|
91
88
|
col_type=ColType.GROUP_BY,
|
|
92
|
-
views=(View.GAME, View.DRAFT),
|
|
93
89
|
expr=pl.col("draft_time").str.to_datetime("%Y-%m-%d %H:%M:%S").dt.hour(),
|
|
94
90
|
dependencies=[ColName.DRAFT_TIME],
|
|
95
91
|
),
|
|
96
92
|
ColumnSpec(
|
|
97
93
|
name=ColName.DRAFT_WEEK,
|
|
98
94
|
col_type=ColType.GROUP_BY,
|
|
99
|
-
views=(View.GAME, View.DRAFT),
|
|
100
95
|
expr=pl.col("draft_time").str.to_datetime("%Y-%m-%d %H:%M:%S").dt.week(),
|
|
101
96
|
dependencies=[ColName.DRAFT_TIME],
|
|
102
97
|
),
|
|
103
98
|
ColumnSpec(
|
|
104
99
|
name=ColName.RANK,
|
|
105
100
|
col_type=ColType.GROUP_BY,
|
|
106
|
-
views=
|
|
101
|
+
views=[View.GAME, View.DRAFT],
|
|
107
102
|
),
|
|
108
103
|
ColumnSpec(
|
|
109
104
|
name=ColName.USER_N_GAMES_BUCKET,
|
|
110
105
|
col_type=ColType.GROUP_BY,
|
|
111
|
-
views=
|
|
106
|
+
views=[View.DRAFT, View.GAME],
|
|
112
107
|
),
|
|
113
108
|
ColumnSpec(
|
|
114
109
|
name=ColName.USER_GAME_WIN_RATE_BUCKET,
|
|
115
110
|
col_type=ColType.GROUP_BY,
|
|
116
|
-
views=
|
|
111
|
+
views=[View.DRAFT, View.GAME],
|
|
117
112
|
),
|
|
118
113
|
ColumnSpec(
|
|
119
114
|
name=ColName.PLAYER_COHORT,
|
|
120
115
|
col_type=ColType.GROUP_BY,
|
|
121
|
-
views=(View.DRAFT, View.GAME),
|
|
122
116
|
expr=pl.when(pl.col("user_n_games_bucket") < 100)
|
|
123
117
|
.then(pl.lit("Other"))
|
|
124
118
|
.otherwise(
|
|
@@ -135,45 +129,41 @@ _column_specs = [
|
|
|
135
129
|
ColumnSpec(
|
|
136
130
|
name=ColName.EVENT_MATCH_WINS,
|
|
137
131
|
col_type=ColType.GROUP_BY,
|
|
138
|
-
views=
|
|
132
|
+
views=[View.DRAFT],
|
|
139
133
|
),
|
|
140
134
|
ColumnSpec(
|
|
141
135
|
name=ColName.EVENT_MATCH_WINS_SUM,
|
|
142
136
|
col_type=ColType.PICK_SUM,
|
|
143
|
-
views=
|
|
137
|
+
views=[View.DRAFT],
|
|
144
138
|
expr=pl.col(ColName.EVENT_MATCH_WINS),
|
|
145
139
|
dependencies=[ColName.EVENT_MATCH_WINS],
|
|
146
140
|
),
|
|
147
141
|
ColumnSpec(
|
|
148
142
|
name=ColName.EVENT_MATCH_LOSSES,
|
|
149
143
|
col_type=ColType.GROUP_BY,
|
|
150
|
-
views=
|
|
144
|
+
views=[View.DRAFT],
|
|
151
145
|
),
|
|
152
146
|
ColumnSpec(
|
|
153
147
|
name=ColName.EVENT_MATCH_LOSSES_SUM,
|
|
154
148
|
col_type=ColType.PICK_SUM,
|
|
155
|
-
views=(View.DRAFT,),
|
|
156
149
|
expr=pl.col(ColName.EVENT_MATCH_LOSSES),
|
|
157
150
|
dependencies=[ColName.EVENT_MATCH_LOSSES],
|
|
158
151
|
),
|
|
159
152
|
ColumnSpec(
|
|
160
153
|
name=ColName.EVENT_MATCHES,
|
|
161
154
|
col_type=ColType.GROUP_BY,
|
|
162
|
-
views=(View.DRAFT,),
|
|
163
155
|
expr=pl.col("event_match_wins") + pl.col("event_match_losses"),
|
|
164
156
|
dependencies=[ColName.EVENT_MATCH_WINS, ColName.EVENT_MATCH_LOSSES],
|
|
165
157
|
),
|
|
166
158
|
ColumnSpec(
|
|
167
159
|
name=ColName.EVENT_MATCHES_SUM,
|
|
168
160
|
col_type=ColType.PICK_SUM,
|
|
169
|
-
views=(View.DRAFT,),
|
|
170
161
|
expr=pl.col(ColName.EVENT_MATCHES),
|
|
171
162
|
dependencies=[ColName.EVENT_MATCHES],
|
|
172
163
|
),
|
|
173
164
|
ColumnSpec(
|
|
174
165
|
name=ColName.IS_TROPHY,
|
|
175
166
|
col_type=ColType.GROUP_BY,
|
|
176
|
-
views=(View.DRAFT,),
|
|
177
167
|
expr=pl.when(pl.col("event_type") == "Traditional")
|
|
178
168
|
.then(pl.col("event_match_wins") == 3)
|
|
179
169
|
.otherwise(pl.col("event_match_wins") == 7),
|
|
@@ -182,45 +172,40 @@ _column_specs = [
|
|
|
182
172
|
ColumnSpec(
|
|
183
173
|
name=ColName.IS_TROPHY_SUM,
|
|
184
174
|
col_type=ColType.PICK_SUM,
|
|
185
|
-
views=(View.DRAFT,),
|
|
186
175
|
expr=pl.col(ColName.IS_TROPHY),
|
|
187
176
|
dependencies=[ColName.IS_TROPHY],
|
|
188
177
|
),
|
|
189
178
|
ColumnSpec(
|
|
190
179
|
name=ColName.PACK_NUMBER,
|
|
191
180
|
col_type=ColType.FILTER_ONLY, # use pack_num
|
|
192
|
-
views=
|
|
181
|
+
views=[View.DRAFT],
|
|
193
182
|
),
|
|
194
183
|
ColumnSpec(
|
|
195
184
|
name=ColName.PACK_NUM,
|
|
196
185
|
col_type=ColType.GROUP_BY,
|
|
197
|
-
views=(View.DRAFT,),
|
|
198
186
|
expr=pl.col("pack_number") + 1,
|
|
199
187
|
dependencies=[ColName.PACK_NUMBER],
|
|
200
188
|
),
|
|
201
189
|
ColumnSpec(
|
|
202
190
|
name=ColName.PICK_NUMBER,
|
|
203
191
|
col_type=ColType.FILTER_ONLY, # use pick_num
|
|
204
|
-
views=
|
|
192
|
+
views=[View.DRAFT],
|
|
205
193
|
),
|
|
206
194
|
ColumnSpec(
|
|
207
195
|
name=ColName.PICK_NUM,
|
|
208
196
|
col_type=ColType.GROUP_BY,
|
|
209
|
-
views=(View.DRAFT,),
|
|
210
197
|
expr=pl.col("pick_number") + 1,
|
|
211
198
|
dependencies=[ColName.PICK_NUMBER],
|
|
212
199
|
),
|
|
213
200
|
ColumnSpec(
|
|
214
201
|
name=ColName.TAKEN_AT,
|
|
215
202
|
col_type=ColType.PICK_SUM,
|
|
216
|
-
views=(View.DRAFT,),
|
|
217
203
|
expr=pl.col(ColName.PICK_NUM),
|
|
218
204
|
dependencies=[ColName.PICK_NUM],
|
|
219
205
|
),
|
|
220
206
|
ColumnSpec(
|
|
221
207
|
name=ColName.NUM_TAKEN,
|
|
222
208
|
col_type=ColType.PICK_SUM,
|
|
223
|
-
views=(View.DRAFT,),
|
|
224
209
|
expr=pl.when(pl.col(ColName.PICK).is_not_null())
|
|
225
210
|
.then(1)
|
|
226
211
|
.otherwise(0), # a literal returns one row under select alone
|
|
@@ -229,27 +214,26 @@ _column_specs = [
|
|
|
229
214
|
ColumnSpec(
|
|
230
215
|
name=ColName.PICK,
|
|
231
216
|
col_type=ColType.FILTER_ONLY, # aggregated as "name"
|
|
232
|
-
views=
|
|
217
|
+
views=[View.DRAFT],
|
|
233
218
|
),
|
|
234
219
|
ColumnSpec(
|
|
235
220
|
name=ColName.PICK_MAINDECK_RATE,
|
|
236
221
|
col_type=ColType.PICK_SUM,
|
|
237
|
-
views=
|
|
222
|
+
views=[View.DRAFT],
|
|
238
223
|
),
|
|
239
224
|
ColumnSpec(
|
|
240
225
|
name=ColName.PICK_SIDEBOARD_IN_RATE,
|
|
241
226
|
col_type=ColType.PICK_SUM,
|
|
242
|
-
views=
|
|
227
|
+
views=[View.DRAFT],
|
|
243
228
|
),
|
|
244
229
|
ColumnSpec(
|
|
245
230
|
name=ColName.PACK_CARD,
|
|
246
231
|
col_type=ColType.NAME_SUM,
|
|
247
|
-
views=
|
|
232
|
+
views=[View.DRAFT],
|
|
248
233
|
),
|
|
249
234
|
ColumnSpec(
|
|
250
235
|
name=ColName.LAST_SEEN,
|
|
251
236
|
col_type=ColType.NAME_SUM,
|
|
252
|
-
views=(View.DRAFT,),
|
|
253
237
|
exprMap=lambda name: pl.col(f"pack_card_{name}")
|
|
254
238
|
* pl.min_horizontal("pick_num", 8),
|
|
255
239
|
dependencies=[ColName.PACK_CARD, ColName.PICK_NUM],
|
|
@@ -257,117 +241,107 @@ _column_specs = [
|
|
|
257
241
|
ColumnSpec(
|
|
258
242
|
name=ColName.NUM_SEEN,
|
|
259
243
|
col_type=ColType.NAME_SUM,
|
|
260
|
-
views=(View.DRAFT,),
|
|
261
244
|
exprMap=lambda name: pl.col(f"pack_card_{name}") * (pl.col("pick_num") <= 8),
|
|
262
245
|
dependencies=[ColName.PACK_CARD, ColName.PICK_NUM],
|
|
263
246
|
),
|
|
264
247
|
ColumnSpec(
|
|
265
248
|
name=ColName.POOL,
|
|
266
249
|
col_type=ColType.NAME_SUM,
|
|
267
|
-
views=
|
|
250
|
+
views=[View.DRAFT],
|
|
268
251
|
),
|
|
269
252
|
ColumnSpec(
|
|
270
253
|
name=ColName.GAME_TIME,
|
|
271
254
|
col_type=ColType.FILTER_ONLY,
|
|
272
|
-
views=
|
|
255
|
+
views=[View.GAME],
|
|
273
256
|
),
|
|
274
257
|
ColumnSpec(
|
|
275
258
|
name=ColName.GAME_DATE,
|
|
276
259
|
col_type=ColType.GROUP_BY,
|
|
277
|
-
views=(View.GAME,),
|
|
278
260
|
expr=pl.col("game_time").str.to_datetime("%Y-%m-%d %H-%M-%S").dt.date(),
|
|
279
261
|
dependencies=[ColName.GAME_TIME],
|
|
280
262
|
),
|
|
281
263
|
ColumnSpec(
|
|
282
264
|
name=ColName.GAME_DAY_OF_WEEK,
|
|
283
265
|
col_type=ColType.GROUP_BY,
|
|
284
|
-
views=(View.GAME,),
|
|
285
266
|
expr=pl.col("game_time").str.to_datetime("%Y-%m-%d %H-%M-%S").dt.weekday(),
|
|
286
267
|
dependencies=[ColName.GAME_TIME],
|
|
287
268
|
),
|
|
288
269
|
ColumnSpec(
|
|
289
270
|
name=ColName.GAME_HOUR,
|
|
290
271
|
col_type=ColType.GROUP_BY,
|
|
291
|
-
views=(View.GAME,),
|
|
292
272
|
expr=pl.col("game_time").str.to_datetime("%Y-%m-%d %H-%M-%S").dt.hour(),
|
|
293
273
|
dependencies=[ColName.GAME_TIME],
|
|
294
274
|
),
|
|
295
275
|
ColumnSpec(
|
|
296
276
|
name=ColName.GAME_WEEK,
|
|
297
277
|
col_type=ColType.GROUP_BY,
|
|
298
|
-
views=(View.GAME,),
|
|
299
278
|
expr=pl.col("game_time").str.to_datetime("%Y-%m-%d %H-%M-%S").dt.week(),
|
|
300
279
|
dependencies=[ColName.GAME_TIME],
|
|
301
280
|
),
|
|
302
281
|
ColumnSpec(
|
|
303
282
|
name=ColName.BUILD_INDEX,
|
|
304
283
|
col_type=ColType.GROUP_BY,
|
|
305
|
-
views=
|
|
284
|
+
views=[View.GAME],
|
|
306
285
|
),
|
|
307
286
|
ColumnSpec(
|
|
308
287
|
name=ColName.MATCH_NUMBER,
|
|
309
288
|
col_type=ColType.GROUP_BY,
|
|
310
|
-
views=
|
|
289
|
+
views=[View.GAME],
|
|
311
290
|
),
|
|
312
291
|
ColumnSpec(
|
|
313
292
|
name=ColName.GAME_NUMBER,
|
|
314
293
|
col_type=ColType.GROUP_BY,
|
|
315
|
-
views=
|
|
294
|
+
views=[View.GAME],
|
|
316
295
|
),
|
|
317
296
|
ColumnSpec(
|
|
318
297
|
name=ColName.NUM_GAMES,
|
|
319
298
|
col_type=ColType.GAME_SUM,
|
|
320
|
-
views=(View.GAME,),
|
|
321
299
|
expr=pl.col(ColName.GAME_NUMBER).is_not_null(),
|
|
322
300
|
dependencies=[ColName.GAME_NUMBER],
|
|
323
301
|
),
|
|
324
302
|
ColumnSpec(
|
|
325
303
|
name=ColName.NUM_MATCHES,
|
|
326
304
|
col_type=ColType.GAME_SUM,
|
|
327
|
-
views=(View.GAME,),
|
|
328
305
|
expr=pl.col(ColName.GAME_NUMBER) == 1,
|
|
329
306
|
dependencies=[ColName.GAME_NUMBER],
|
|
330
307
|
),
|
|
331
308
|
ColumnSpec(
|
|
332
309
|
name=ColName.NUM_EVENTS,
|
|
333
310
|
col_type=ColType.GAME_SUM,
|
|
334
|
-
views=(View.GAME,),
|
|
335
311
|
expr=(pl.col(ColName.GAME_NUMBER) == 1) & (pl.col(ColName.MATCH_NUMBER) == 1),
|
|
336
312
|
dependencies=[ColName.GAME_NUMBER, ColName.MATCH_NUMBER],
|
|
337
313
|
),
|
|
338
314
|
ColumnSpec(
|
|
339
315
|
name=ColName.OPP_RANK,
|
|
340
316
|
col_type=ColType.GROUP_BY,
|
|
341
|
-
views=
|
|
317
|
+
views=[View.GAME],
|
|
342
318
|
),
|
|
343
319
|
ColumnSpec(
|
|
344
320
|
name=ColName.MAIN_COLORS,
|
|
345
321
|
col_type=ColType.GROUP_BY,
|
|
346
|
-
views=
|
|
322
|
+
views=[View.GAME],
|
|
347
323
|
),
|
|
348
324
|
ColumnSpec(
|
|
349
325
|
name=ColName.NUM_COLORS,
|
|
350
326
|
col_type=ColType.GROUP_BY,
|
|
351
|
-
views=(View.GAME,),
|
|
352
327
|
expr=pl.col(ColName.MAIN_COLORS).str.len_chars(),
|
|
353
328
|
dependencies=[ColName.MAIN_COLORS],
|
|
354
329
|
),
|
|
355
330
|
ColumnSpec(
|
|
356
331
|
name=ColName.SPLASH_COLORS,
|
|
357
332
|
col_type=ColType.GROUP_BY,
|
|
358
|
-
views=
|
|
333
|
+
views=[View.GAME],
|
|
359
334
|
),
|
|
360
335
|
ColumnSpec(
|
|
361
336
|
name=ColName.HAS_SPLASH,
|
|
362
337
|
col_type=ColType.GROUP_BY,
|
|
363
|
-
views=(View.GAME,),
|
|
364
338
|
expr=pl.col(ColName.SPLASH_COLORS).str.len_chars() > 0,
|
|
365
339
|
dependencies=[ColName.SPLASH_COLORS],
|
|
366
340
|
),
|
|
367
341
|
ColumnSpec(
|
|
368
342
|
name=ColName.ON_PLAY,
|
|
369
343
|
col_type=ColType.GROUP_BY,
|
|
370
|
-
views=
|
|
344
|
+
views=[View.GAME],
|
|
371
345
|
),
|
|
372
346
|
ColumnSpec(
|
|
373
347
|
name=ColName.NUM_ON_PLAY,
|
|
@@ -378,120 +352,110 @@ _column_specs = [
|
|
|
378
352
|
ColumnSpec(
|
|
379
353
|
name=ColName.NUM_MULLIGANS,
|
|
380
354
|
col_type=ColType.GROUP_BY,
|
|
381
|
-
views=
|
|
355
|
+
views=[View.GAME],
|
|
382
356
|
),
|
|
383
357
|
ColumnSpec(
|
|
384
358
|
name=ColName.NUM_MULLIGANS_SUM,
|
|
385
359
|
col_type=ColType.GAME_SUM,
|
|
386
|
-
views=(View.GAME,),
|
|
387
360
|
expr=pl.col(ColName.NUM_MULLIGANS),
|
|
388
361
|
dependencies=[ColName.NUM_MULLIGANS],
|
|
389
362
|
),
|
|
390
363
|
ColumnSpec(
|
|
391
364
|
name=ColName.OPP_NUM_MULLIGANS,
|
|
392
365
|
col_type=ColType.GAME_SUM,
|
|
393
|
-
views=
|
|
366
|
+
views=[View.GAME],
|
|
394
367
|
),
|
|
395
368
|
ColumnSpec(
|
|
396
369
|
name=ColName.OPP_NUM_MULLIGANS_SUM,
|
|
397
370
|
col_type=ColType.GAME_SUM,
|
|
398
|
-
views=(View.GAME,),
|
|
399
371
|
expr=pl.col(ColName.OPP_NUM_MULLIGANS),
|
|
400
372
|
dependencies=[ColName.OPP_NUM_MULLIGANS],
|
|
401
373
|
),
|
|
402
374
|
ColumnSpec(
|
|
403
375
|
name=ColName.OPP_COLORS,
|
|
404
376
|
col_type=ColType.GROUP_BY,
|
|
405
|
-
views=
|
|
377
|
+
views=[View.GAME],
|
|
406
378
|
),
|
|
407
379
|
ColumnSpec(
|
|
408
380
|
name=ColName.NUM_TURNS,
|
|
409
381
|
col_type=ColType.GROUP_BY,
|
|
410
|
-
views=
|
|
382
|
+
views=[View.GAME],
|
|
411
383
|
),
|
|
412
384
|
ColumnSpec(
|
|
413
385
|
name=ColName.NUM_TURNS_SUM,
|
|
414
386
|
col_type=ColType.GAME_SUM,
|
|
415
|
-
views=(View.GAME,),
|
|
416
387
|
expr=pl.col(ColName.NUM_TURNS),
|
|
417
388
|
dependencies=[ColName.NUM_TURNS],
|
|
418
389
|
),
|
|
419
390
|
ColumnSpec(
|
|
420
391
|
name=ColName.WON,
|
|
421
392
|
col_type=ColType.GROUP_BY,
|
|
422
|
-
views=
|
|
393
|
+
views=[View.GAME],
|
|
423
394
|
),
|
|
424
395
|
ColumnSpec(
|
|
425
396
|
name=ColName.NUM_WON,
|
|
426
397
|
col_type=ColType.GAME_SUM,
|
|
427
|
-
views=(View.GAME,),
|
|
428
398
|
expr=pl.col(ColName.WON),
|
|
429
399
|
dependencies=[ColName.WON],
|
|
430
400
|
),
|
|
431
401
|
ColumnSpec(
|
|
432
402
|
name=ColName.OPENING_HAND,
|
|
433
403
|
col_type=ColType.NAME_SUM,
|
|
434
|
-
views=
|
|
404
|
+
views=[View.GAME],
|
|
435
405
|
),
|
|
436
406
|
ColumnSpec(
|
|
437
407
|
name=ColName.WON_OPENING_HAND,
|
|
438
408
|
col_type=ColType.NAME_SUM,
|
|
439
|
-
views=(View.GAME,),
|
|
440
409
|
exprMap=lambda name: pl.col(f"opening_hand_{name}") * pl.col(ColName.WON),
|
|
441
410
|
dependencies=[ColName.OPENING_HAND, ColName.WON],
|
|
442
411
|
),
|
|
443
412
|
ColumnSpec(
|
|
444
413
|
name=ColName.DRAWN,
|
|
445
414
|
col_type=ColType.NAME_SUM,
|
|
446
|
-
views=
|
|
415
|
+
views=[View.GAME],
|
|
447
416
|
),
|
|
448
417
|
ColumnSpec(
|
|
449
418
|
name=ColName.WON_DRAWN,
|
|
450
419
|
col_type=ColType.NAME_SUM,
|
|
451
|
-
views=(View.GAME,),
|
|
452
420
|
exprMap=lambda name: pl.col(f"drawn_{name}") * pl.col(ColName.WON),
|
|
453
421
|
dependencies=[ColName.DRAWN, ColName.WON],
|
|
454
422
|
),
|
|
455
423
|
ColumnSpec(
|
|
456
424
|
name=ColName.TUTORED,
|
|
457
425
|
col_type=ColType.NAME_SUM,
|
|
458
|
-
views=
|
|
426
|
+
views=[View.GAME],
|
|
459
427
|
),
|
|
460
428
|
ColumnSpec(
|
|
461
429
|
name=ColName.WON_TUTORED,
|
|
462
430
|
col_type=ColType.NAME_SUM,
|
|
463
|
-
views=(View.GAME,),
|
|
464
431
|
exprMap=lambda name: pl.col(f"tutored_{name}") * pl.col(ColName.WON),
|
|
465
432
|
dependencies=[ColName.TUTORED, ColName.WON],
|
|
466
433
|
),
|
|
467
434
|
ColumnSpec(
|
|
468
435
|
name=ColName.DECK,
|
|
469
436
|
col_type=ColType.NAME_SUM,
|
|
470
|
-
views=
|
|
437
|
+
views=[View.GAME],
|
|
471
438
|
),
|
|
472
439
|
ColumnSpec(
|
|
473
440
|
name=ColName.WON_DECK,
|
|
474
441
|
col_type=ColType.NAME_SUM,
|
|
475
|
-
views=(View.GAME,),
|
|
476
442
|
exprMap=lambda name: pl.col(f"deck_{name}") * pl.col(ColName.WON),
|
|
477
443
|
dependencies=[ColName.DECK, ColName.WON],
|
|
478
444
|
),
|
|
479
445
|
ColumnSpec(
|
|
480
446
|
name=ColName.SIDEBOARD,
|
|
481
447
|
col_type=ColType.NAME_SUM,
|
|
482
|
-
views=
|
|
448
|
+
views=[View.GAME],
|
|
483
449
|
),
|
|
484
450
|
ColumnSpec(
|
|
485
451
|
name=ColName.WON_SIDEBOARD,
|
|
486
452
|
col_type=ColType.NAME_SUM,
|
|
487
|
-
views=(View.GAME,),
|
|
488
453
|
exprMap=lambda name: pl.col(f"sideboard_{name}") * pl.col(ColName.WON),
|
|
489
454
|
dependencies=[ColName.SIDEBOARD, ColName.WON],
|
|
490
455
|
),
|
|
491
456
|
ColumnSpec(
|
|
492
457
|
name=ColName.NUM_GNS,
|
|
493
458
|
col_type=ColType.NAME_SUM,
|
|
494
|
-
views=(View.GAME,),
|
|
495
459
|
exprMap=lambda name: pl.max_horizontal(
|
|
496
460
|
0,
|
|
497
461
|
pl.col(f"deck_{name}")
|
|
@@ -509,69 +473,64 @@ _column_specs = [
|
|
|
509
473
|
ColumnSpec(
|
|
510
474
|
name=ColName.WON_NUM_GNS,
|
|
511
475
|
col_type=ColType.NAME_SUM,
|
|
512
|
-
views=(View.GAME,),
|
|
513
476
|
exprMap=lambda name: pl.col(ColName.WON) * pl.col(f"num_gns_{name}"),
|
|
514
477
|
dependencies=[ColName.NUM_GNS, ColName.WON],
|
|
515
478
|
),
|
|
516
479
|
ColumnSpec(
|
|
517
480
|
name=ColName.SET_CODE,
|
|
518
481
|
col_type=ColType.CARD_ATTR,
|
|
519
|
-
views=(View.CARD,),
|
|
520
482
|
),
|
|
521
483
|
ColumnSpec(
|
|
522
484
|
name=ColName.COLOR,
|
|
523
485
|
col_type=ColType.CARD_ATTR,
|
|
524
|
-
views=(View.CARD,),
|
|
525
486
|
),
|
|
526
487
|
ColumnSpec(
|
|
527
488
|
name=ColName.RARITY,
|
|
528
489
|
col_type=ColType.CARD_ATTR,
|
|
529
|
-
views=(View.CARD,),
|
|
530
490
|
),
|
|
531
491
|
ColumnSpec(
|
|
532
492
|
name=ColName.COLOR_IDENTITY,
|
|
533
493
|
col_type=ColType.CARD_ATTR,
|
|
534
|
-
views=(View.CARD,),
|
|
535
494
|
),
|
|
536
495
|
ColumnSpec(
|
|
537
496
|
name=ColName.CARD_TYPE,
|
|
538
497
|
col_type=ColType.CARD_ATTR,
|
|
539
|
-
views=(View.CARD,),
|
|
540
498
|
),
|
|
541
499
|
ColumnSpec(
|
|
542
500
|
name=ColName.SUBTYPE,
|
|
543
501
|
col_type=ColType.CARD_ATTR,
|
|
544
|
-
views=(View.CARD,),
|
|
545
502
|
),
|
|
546
503
|
ColumnSpec(
|
|
547
504
|
name=ColName.MANA_VALUE,
|
|
548
505
|
col_type=ColType.CARD_ATTR,
|
|
549
|
-
views=(View.CARD,),
|
|
550
506
|
),
|
|
551
507
|
ColumnSpec(
|
|
552
508
|
name=ColName.MANA_COST,
|
|
553
509
|
col_type=ColType.CARD_ATTR,
|
|
554
|
-
views=(View.CARD,),
|
|
555
510
|
),
|
|
556
511
|
ColumnSpec(
|
|
557
512
|
name=ColName.POWER,
|
|
558
513
|
col_type=ColType.CARD_ATTR,
|
|
559
|
-
views=(View.CARD,),
|
|
560
514
|
),
|
|
561
515
|
ColumnSpec(
|
|
562
516
|
name=ColName.TOUGHNESS,
|
|
563
517
|
col_type=ColType.CARD_ATTR,
|
|
564
|
-
views=(View.CARD,),
|
|
565
518
|
),
|
|
566
519
|
ColumnSpec(
|
|
567
520
|
name=ColName.IS_BONUS_SHEET,
|
|
568
521
|
col_type=ColType.CARD_ATTR,
|
|
569
|
-
views=(View.CARD,),
|
|
570
522
|
),
|
|
571
523
|
ColumnSpec(
|
|
572
524
|
name=ColName.IS_DFC,
|
|
573
525
|
col_type=ColType.CARD_ATTR,
|
|
574
|
-
|
|
526
|
+
),
|
|
527
|
+
ColumnSpec(
|
|
528
|
+
name=ColName.ORACLE_TEXT,
|
|
529
|
+
col_type=ColType.CARD_ATTR,
|
|
530
|
+
),
|
|
531
|
+
ColumnSpec(
|
|
532
|
+
name=ColName.CARD_JSON,
|
|
533
|
+
col_type=ColType.CARD_ATTR,
|
|
575
534
|
),
|
|
576
535
|
ColumnSpec(
|
|
577
536
|
name=ColName.PICKED_MATCH_WR,
|
|
@@ -594,7 +553,6 @@ _column_specs = [
|
|
|
594
553
|
ColumnSpec(
|
|
595
554
|
name=ColName.ALSA,
|
|
596
555
|
col_type=ColType.AGG,
|
|
597
|
-
views=(),
|
|
598
556
|
expr=pl.col(ColName.LAST_SEEN) / pl.col(ColName.NUM_SEEN),
|
|
599
557
|
dependencies=[ColName.LAST_SEEN, ColName.NUM_SEEN],
|
|
600
558
|
),
|
spells/draft_data.py
CHANGED
|
@@ -53,6 +53,23 @@ def _get_names(set_code: str) -> tuple[str, ...]:
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
def _hydrate_col_defs(set_code: str, col_spec_map: dict[str, ColumnSpec]):
|
|
56
|
+
def get_views(spec: ColumnSpec) -> list[View]:
|
|
57
|
+
if spec.name == ColName.NAME or spec.col_type == ColType.AGG:
|
|
58
|
+
return []
|
|
59
|
+
if spec.col_type == ColType.CARD_ATTR:
|
|
60
|
+
return [View.CARD]
|
|
61
|
+
if spec.views is not None:
|
|
62
|
+
return spec.views
|
|
63
|
+
assert (
|
|
64
|
+
spec.dependencies is not None
|
|
65
|
+
), f"Col {spec.name} should have dependencies"
|
|
66
|
+
|
|
67
|
+
views = []
|
|
68
|
+
for dep in spec.dependencies:
|
|
69
|
+
views.extend(get_views(col_spec_map[dep]))
|
|
70
|
+
|
|
71
|
+
return list(set(views))
|
|
72
|
+
|
|
56
73
|
names = _get_names(set_code)
|
|
57
74
|
assert len(names) > 0, "there should be names"
|
|
58
75
|
hydrated = {}
|
|
@@ -87,19 +104,21 @@ def _hydrate_col_defs(set_code: str, col_spec_map: dict[str, ColumnSpec]):
|
|
|
87
104
|
expr_sig = str(datetime.datetime.now)
|
|
88
105
|
|
|
89
106
|
dependencies = tuple(spec.dependencies or ())
|
|
107
|
+
views = get_views(spec)
|
|
90
108
|
signature = str(
|
|
91
109
|
(
|
|
92
110
|
spec.name,
|
|
93
111
|
spec.col_type.value,
|
|
94
112
|
expr_sig,
|
|
95
|
-
tuple(view.value for view in
|
|
113
|
+
tuple(view.value for view in views),
|
|
96
114
|
dependencies,
|
|
97
115
|
)
|
|
98
116
|
)
|
|
117
|
+
|
|
99
118
|
cdef = ColumnDefinition(
|
|
100
119
|
name=spec.name,
|
|
101
120
|
col_type=spec.col_type,
|
|
102
|
-
views=
|
|
121
|
+
views=tuple(views),
|
|
103
122
|
expr=expr,
|
|
104
123
|
dependencies=dependencies,
|
|
105
124
|
signature=signature,
|
|
@@ -246,6 +265,28 @@ def _base_agg_df(
|
|
|
246
265
|
)
|
|
247
266
|
|
|
248
267
|
|
|
268
|
+
def card_df(
|
|
269
|
+
set_code: str,
|
|
270
|
+
extensions: list[ColumnSpec] | None = None,
|
|
271
|
+
):
|
|
272
|
+
col_spec_map = dict(spells.columns.col_spec_map)
|
|
273
|
+
if extensions is not None:
|
|
274
|
+
for spec in extensions:
|
|
275
|
+
col_spec_map[spec.name] = spec
|
|
276
|
+
|
|
277
|
+
col_def_map = _hydrate_col_defs(set_code, col_spec_map)
|
|
278
|
+
|
|
279
|
+
columns = [ColName.NAME] + [
|
|
280
|
+
c for c, cdef in col_def_map.items() if cdef.col_type == ColType.CARD_ATTR
|
|
281
|
+
]
|
|
282
|
+
fp = data_file_path(set_code, View.CARD)
|
|
283
|
+
card_df = pl.read_parquet(fp)
|
|
284
|
+
select_df = _view_select(
|
|
285
|
+
card_df, frozenset(columns), col_def_map, is_agg_view=False
|
|
286
|
+
)
|
|
287
|
+
return select_df.select(columns)
|
|
288
|
+
|
|
289
|
+
|
|
249
290
|
def summon(
|
|
250
291
|
set_code: str,
|
|
251
292
|
columns: list[str] | None = None,
|
spells/enums.py
CHANGED
|
@@ -120,6 +120,8 @@ class ColName(StrEnum):
|
|
|
120
120
|
TOUGHNESS = "toughness"
|
|
121
121
|
IS_BONUS_SHEET = "is_bonus_sheet"
|
|
122
122
|
IS_DFC = "is_dfc"
|
|
123
|
+
ORACLE_TEXT = "oracle_text"
|
|
124
|
+
CARD_JSON = "card_json"
|
|
123
125
|
# agg extensions
|
|
124
126
|
PICKED_MATCH_WR = "picked_match_wr"
|
|
125
127
|
TROPHY_RATE = "trophy_rate"
|
spells/external.py
CHANGED
|
@@ -268,7 +268,7 @@ def download_data_set(
|
|
|
268
268
|
mode, "Unzipping and transforming to parquet (this might take a few minutes)..."
|
|
269
269
|
)
|
|
270
270
|
_process_zipped_file(dataset_path, target_path)
|
|
271
|
-
cache.spells_print(mode, f"
|
|
271
|
+
cache.spells_print(mode, f"Wrote file {target_path}")
|
|
272
272
|
if clear_set_cache:
|
|
273
273
|
cache.clear(set_code)
|
|
274
274
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: spells-mtg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: analaysis of 17Lands.com public datasets
|
|
5
5
|
Author-Email: Joel Barnes <oelarnes@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -19,16 +19,16 @@ $ spells add DSK
|
|
|
19
19
|
|
|
20
20
|
🪄 add ✨ Downloading draft dataset from 17Lands.com
|
|
21
21
|
100% [......................................................................] 250466473 / 250466473
|
|
22
|
-
🪄 add ✨ Unzipping and transforming to parquet...
|
|
23
|
-
🪄 add ✨
|
|
22
|
+
🪄 add ✨ Unzipping and transforming to parquet (this might take a few minutes)...
|
|
23
|
+
🪄 add ✨ Wrote file /Users/joel/.local/share/spells/external/DSK/DSK_PremierDraft_draft.parquet
|
|
24
24
|
🪄 clean ✨ No local cache found for set DSK
|
|
25
|
+
🪄 add ✨ Fetching card data from mtgjson.com and writing card parquet file
|
|
26
|
+
🪄 add ✨ Wrote file /Users/joel/.local/share/spells/external/DSK/DSK_card.parquet
|
|
25
27
|
🪄 add ✨ Downloading game dataset from 17Lands.com
|
|
26
28
|
100% [........................................................................] 77145600 / 77145600
|
|
27
|
-
🪄 add ✨ Unzipping and transforming to parquet...
|
|
28
|
-
🪄 add ✨
|
|
29
|
+
🪄 add ✨ Unzipping and transforming to parquet (this might take a few minutes)...
|
|
30
|
+
🪄 add ✨ Wrote file /Users/joel/.local/share/spells/external/DSK/DSK_PremierDraft_game.parquet
|
|
29
31
|
🪄 clean ✨ No local cache found for set DSK
|
|
30
|
-
🪄 add ✨ Fetching card data from mtgjson.com and writing card parquet file
|
|
31
|
-
🪄 add ✨ Wrote 287 lines to file /Users/joel/.local/share/spells/external/DSK/DSK_card.parquet
|
|
32
32
|
$ ipython
|
|
33
33
|
```
|
|
34
34
|
|
|
@@ -64,7 +64,7 @@ Wall time: 80.8 ms
|
|
|
64
64
|
```
|
|
65
65
|
Coverting to pandas DataFrame is as simple as invoking the chained call `summon(...).to_pandas()`.
|
|
66
66
|
|
|
67
|
-
Spells is not affiliated with 17Lands. Please review the Usage Guidelines for 17lands data before using Spells, and consider supporting their patreon. Spells is free and open-source; please consider contributing and feel free to make use of the source code under the terms of the MIT license.
|
|
67
|
+
Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](https://www.17lands.com/usage_guidelines) for 17lands data before using Spells, and consider supporting their patreon. Spells is free and open-source; please consider contributing and feel free to make use of the source code under the terms of the MIT license.
|
|
68
68
|
|
|
69
69
|
## spells
|
|
70
70
|
|
|
@@ -74,7 +74,7 @@ Spells is not affiliated with 17Lands. Please review the Usage Guidelines for 17
|
|
|
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 118 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
|
|
@@ -82,7 +82,8 @@ Spells is not affiliated with 17Lands. Please review the Usage Guidelines for 17
|
|
|
82
82
|
- Is fully typed, linted, and statically analyzed for support of advanced IDE features
|
|
83
83
|
- Provides optional enums for all base columns and built-in extensions, as well as for custom extension parameters
|
|
84
84
|
- Uses Polars expressions to support second-stage aggregations and beyond like game-weighted z-scores with one call to summon
|
|
85
|
-
-
|
|
85
|
+
- Works on MacOS, Linux, and Windows
|
|
86
|
+
- Provides example notebooks to kickstart your exploration
|
|
86
87
|
|
|
87
88
|
## summon
|
|
88
89
|
|
|
@@ -133,7 +134,7 @@ Spells is not affiliated with 17Lands. Please review the Usage Guidelines for 17
|
|
|
133
134
|
- `filter_spec` specifies a row-level filter for the dataset, using an intuitive custom query formulation
|
|
134
135
|
```python
|
|
135
136
|
>>> from spells.enums import ColName
|
|
136
|
-
>>> spells.summon('BLB', columns=[
|
|
137
|
+
>>> spells.summon('BLB', columns=[ColName.GAME_WR], group_by=[ColName.PLAYER_COHORT], filter_spec={'lhs': ColName.NUM_MULLIGANS, 'op': '>', 'rhs': 0})
|
|
137
138
|
shape: (4, 2)
|
|
138
139
|
┌───────────────┬──────────┐
|
|
139
140
|
│ player_cohort ┆ game_wr │
|
|
@@ -193,6 +194,8 @@ Spells is still in development and could benefit from many new features and impr
|
|
|
193
194
|
|
|
194
195
|
If you are new to Python, I recommend using a package manager like poetry, pdm or uv to create a virtual environment and manage your project.
|
|
195
196
|
|
|
197
|
+
Once Spells is installed, check out the notebooks under [examples](https://github.com/oelarnes/spells/tree/main/examples) for ideas on getting started.
|
|
198
|
+
|
|
196
199
|
## Why did you make this? Who is it for?
|
|
197
200
|
|
|
198
201
|
Earlier this year I developed a card quality metric called [DEq](https://docs.google.com/spreadsheets/d/1n1pfrb5q_2ICYk-vfF3Uwo8t61DJU-5T_DFe0dwk8DY/edit), short for "Estimated Draft Equity", which is designed to estimate the average value of selecting a card in draft relative to a comparable baseline, in order to improve on commonly-used metrics like GIH WR, which has a number of major and minor problems when interpreted as a card quality metric. DEq depends on the daily drops from 17Lands.com and won't be replaced by this static kind of analysis.
|
|
@@ -205,7 +208,7 @@ If you're interested in the fruits of my DEq research, or in checking my work, k
|
|
|
205
208
|
|
|
206
209
|
## Performance
|
|
207
210
|
|
|
208
|
-
Spells provides several features
|
|
211
|
+
Spells provides several features to optimize performance.
|
|
209
212
|
|
|
210
213
|
### Parquet Transformation
|
|
211
214
|
|
|
@@ -213,11 +216,11 @@ The most significant optimization used by Spells is the simplest: the csv files
|
|
|
213
216
|
|
|
214
217
|
### Query Optimization
|
|
215
218
|
|
|
216
|
-
|
|
219
|
+
Spells is built on top of Polars, a modern, well-supported DataFrame engine written for performance in Rust that enables declarative query plans and lazy evaluation, allowing for automatic performance optimization in the execution of the query plan. Spells selects only the necessary columns for your analysis, recursively traversing the dependency tree.
|
|
217
220
|
|
|
218
221
|
### Local Caching
|
|
219
222
|
|
|
220
|
-
|
|
223
|
+
Spells caches the results of expensive aggregations in the local file system as parquet files, which by default are found under the `data/local` path from the execution directory, which can be configured using the environment variable `SPELLS_PROJECT_DIR`. Query plans which request the same set of first-stage aggregations (sums over base rows) will attempt to locate the aggregate data in the cache before calculating. This guarantees that a repeated call to `summon` returns instantaneously.
|
|
221
224
|
|
|
222
225
|
When refreshing a given set's data files from 17Lands using the provided cli, the cache for that set is automatically cleared. The `spells` CLI gives additional tools for managing the local and external caches.
|
|
223
226
|
|
|
@@ -249,8 +252,8 @@ So that's it, that's what Spells does from a high level. `summon` will hand off
|
|
|
249
252
|
## CLI
|
|
250
253
|
|
|
251
254
|
Spells includes a command-line interface `spells` to manage your external data files and local cache. Spells will download files to an appropriate file location on your system,
|
|
252
|
-
typically `~/.local/share/spells` on Unix-like platforms and `C:\Users\{Username}\AppData\Local\Spells` on Windows
|
|
253
|
-
To use `spells`, make sure Spells
|
|
255
|
+
typically `~/.local/share/spells` on Unix-like platforms and `C:\Users\{Username}\AppData\Local\Spells` on Windows, or to a location specified by the environment variable `SPELLS_DATA_HOME`.
|
|
256
|
+
To use `spells`, make sure Spells is installed in your environment using pip or a package manager, and type `spells help` into your shell, or dive in with `spells add DSK` or your favorite set. If Spells is installed globally using pipx, any local version of Spells will be able to read the managed files.
|
|
254
257
|
|
|
255
258
|
## API
|
|
256
259
|
|
|
@@ -264,6 +267,8 @@ summon(
|
|
|
264
267
|
group_by: list[str] | None = None,
|
|
265
268
|
filter_spec: dict | None = None,
|
|
266
269
|
extensions: list[str] | None = None,
|
|
270
|
+
read_cache: bool = True,
|
|
271
|
+
write_cache: bool = True,
|
|
267
272
|
) -> polars.DataFrame
|
|
268
273
|
```
|
|
269
274
|
|
|
@@ -283,13 +288,15 @@ aggregations of non-numeric (or numeric) data types are not supported. If `None`
|
|
|
283
288
|
|
|
284
289
|
- extensions: a list of `spells.columns.ColumnSpec` objects, which are appended to the definitions built-in columns described below. A name not in the enum `ColName` can be used in this way if it is the name of a provided extension. Existing names can also be redefined using extensions.
|
|
285
290
|
|
|
291
|
+
- read_cache/write_cache: Use the local file system to cache and retrieve aggregations to minimize expensive reads of the large datasets. You shouldn't need to touch these arguments unless you are debugging.
|
|
292
|
+
|
|
286
293
|
### Enums
|
|
287
294
|
|
|
288
295
|
```python
|
|
289
296
|
from spells.enums import ColName, ColType, View
|
|
290
297
|
```
|
|
291
298
|
|
|
292
|
-
Recommended to import `ColName` for any usage of `summon`, and to import `ColType`
|
|
299
|
+
Recommended to import `ColName` for any usage of `summon`, and to import `ColType` when defining custom extensions. You shouldn't need `VIEW`.
|
|
293
300
|
|
|
294
301
|
### ColumnSpec
|
|
295
302
|
|
|
@@ -298,12 +305,12 @@ from spells.columns import ColumnSpec
|
|
|
298
305
|
|
|
299
306
|
ColumnSpec(
|
|
300
307
|
name: str,
|
|
301
|
-
col_type:
|
|
302
|
-
views: tuple(spells.enums.View...) = (),
|
|
308
|
+
col_type: ColType,
|
|
303
309
|
expr: pl.Expr | None = None,
|
|
304
310
|
exprMap: Callable[[str], pl.Expr] | None = None
|
|
305
311
|
dependencies: list[str] | None = None
|
|
306
312
|
version: str | None = None
|
|
313
|
+
views: list[View] | None = None,
|
|
307
314
|
)
|
|
308
315
|
```
|
|
309
316
|
|
|
@@ -315,8 +322,6 @@ Used to define extensions in `summon`
|
|
|
315
322
|
|
|
316
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` 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 summing over groups, and can include polars Expression aggregations. Arbitrarily long chains of aggregate dependencies are supported.
|
|
317
324
|
|
|
318
|
-
- `views`: For a column defined at the row level on a view (see col_types above), the views on which it is supported. All col_types except `AGG` must specify at least one base view. For `CARD_ATTR` columns, `views` must be exactly `(View.CARD,)`.
|
|
319
|
-
|
|
320
325
|
- `expr`: A polars expression giving the derivation of the column value at the first level where it is defined. For `NAME_SUM` columns the `exprMap` attribute must be used instead. `AGG` columns that depend on `NAME_SUM` columns reference the prefix (`cdef.name`) only, since the unpivot has occured prior to selection.
|
|
321
326
|
|
|
322
327
|
- `exprMap`: A function of card name that returns the expression for a `NAME_SUM` column.
|
|
@@ -326,6 +331,8 @@ Used to define extensions in `summon`
|
|
|
326
331
|
- `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
|
|
327
332
|
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.
|
|
328
333
|
|
|
334
|
+
- `views`: Not needed for custom columns.
|
|
335
|
+
|
|
329
336
|
### Columns
|
|
330
337
|
|
|
331
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.
|
|
@@ -418,6 +425,8 @@ A table of all included columns. Columns can be referenced by enum or by string
|
|
|
418
425
|
| `TOUGHNESS` | `"toughness"` | `CARD` | `CARD_ATTR` | | Float |
|
|
419
426
|
| `IS_BONUS_SHEET` | `"is_bonus_sheet"` | `CARD` | `CARD_ATTR` | `SET_CODE` != `EXPANSION` | Boolean |
|
|
420
427
|
| `IS_DFC` | `"is_dfc"` | `CARD` | `CARD_ATTR` | Includes split cards | Boolean |
|
|
428
|
+
| `ORACLE_TEXT` | `"oracle_text"` | `CARD` | `CARD_ATTR` | | String |
|
|
429
|
+
| `CARD_JSON` | `"card_json"` | `CARD` | `CARD_ATTR` | The full dump of the mtgjson entry for the card as printed in the draft booster | String |
|
|
421
430
|
| `PICKED_MATCH_WR` | `"picked_match_wr"` | | `AGG` | `EVENT_MATCH_WINS` / `EVENT_MATCHES` | Float |
|
|
422
431
|
| `TROPHY_RATE` | `"trophy_rate"` | | `AGG` || Float |
|
|
423
432
|
| `GAME_WR` | `"game_wr"` | | `AGG` | `NUM_WON` / `NUM_GAMES` | Float |
|
|
@@ -444,7 +453,7 @@ A table of all included columns. Columns can be referenced by enum or by string
|
|
|
444
453
|
| `GP_WR_Z` | `"gp_wr_z"` | | `AGG` | `GP_WR_EXCESS` / `GP_WR_STDEV` | Float |
|
|
445
454
|
| `GIH_TOTAL` | `"gih_total"` | | `AGG` | Sum `NUM_GIH` over all rows and broadcast back to row level| Float |
|
|
446
455
|
| `WON_GIH_TOTAL` | `"won_gih_total"` | | `AGG` | | Float |
|
|
447
|
-
| `GIH_WR_MEAN` | `"gih_wr_mean"` | | `AGG` |
|
|
456
|
+
| `GIH_WR_MEAN` | `"gih_wr_mean"` | | `AGG` | `WON_GIH_TOTAL` / `GIH_TOTAL` | Float |
|
|
448
457
|
| `GIH_WR_EXCESS` | `"gih_wr_excess"` | | `AGG` | `GIH_WR - GIH_WR_MEAN` | Float |
|
|
449
458
|
| `GIH_WR_VAR` | `"gih_wr_var"` | | `AGG` | Game-weighted Variance | Float |
|
|
450
459
|
| `GIH_WR_STDEV` | `"gh_wr_stdev"` | | `AGG` | Sqrt of `GIH_WR_VAR` | Float |
|
|
@@ -0,0 +1,15 @@
|
|
|
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=Zw7IIR-HX2HCdeYBGn0RmPGzPA1juGfMl050ndtWhlc,22052
|
|
5
|
+
spells/draft_data.py,sha256=xIr0Jpzv3ktp5AptVFuXlSqTqwGcHE-j1sXNtf064c0,10497
|
|
6
|
+
spells/enums.py,sha256=BQIZvCSAopiscnKY-Qm_j2BgmPy9R5oj1yeIrjMQM1o,4507
|
|
7
|
+
spells/external.py,sha256=qe6wOBDhPN4CZNQvYRq6G-OpIZcWTZzJjySgnf2Gu1o,10258
|
|
8
|
+
spells/filter.py,sha256=J-YTOOAzOQpvIX29tviYL04RVoOUlfsbjBXoQBDCEdQ,3380
|
|
9
|
+
spells/manifest.py,sha256=XEJwwYJzOEWrQQwka39F9SgZo38hH_Kj4MPY7GOuLvY,6903
|
|
10
|
+
spells/schema.py,sha256=z8Qn2SiHG4T6YfPsz8xHLGMjU_Ofm76-Vrquh3b9B64,6422
|
|
11
|
+
spells_mtg-0.2.2.dist-info/METADATA,sha256=zUFcfmHmEp_TD34EYBnFt48qAFOqLNVJv2iKxgFnZIo,41597
|
|
12
|
+
spells_mtg-0.2.2.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
|
13
|
+
spells_mtg-0.2.2.dist-info/entry_points.txt,sha256=a9Y1omdl9MdnKuIj3aOodgrp-zZII6OCdvqwgP6BFvI,63
|
|
14
|
+
spells_mtg-0.2.2.dist-info/licenses/LICENSE,sha256=tS54XYbJSgmq5zuHhbsQGbNQLJPVgXqhF5nu2CSRMig,1068
|
|
15
|
+
spells_mtg-0.2.2.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
spells/__init__.py,sha256=EcI7ijXYvPA8jj7wUZqs6CSWr__MD8AOXhkex-Hj37E,131
|
|
2
|
-
spells/cache.py,sha256=4v7h8D3TtaT0R_EdiRNhdcQrXzdH_CukezO6oAXvNEY,2956
|
|
3
|
-
spells/cards.py,sha256=CP8f3rUBuFQlj6xj2W72-mDQPuzozEcMu3PkRtBeX1Q,3191
|
|
4
|
-
spells/columns.py,sha256=BEYKDpZYkRgV1kBqIq6XC6kXvVjOih7jGthIpy-YVSw,23339
|
|
5
|
-
spells/draft_data.py,sha256=xoGn6QGaAKgIInEzi2pnvk4CH5xNrwYq4mAj3r07GfA,9272
|
|
6
|
-
spells/enums.py,sha256=Idpv17rCAHDF5bENU8I8V0rpL9W5KB2GM6lhrN4GunU,4447
|
|
7
|
-
spells/external.py,sha256=Apv_l32swO9bF8ihWV73cd7zpzHgxqFy8ZZa1n3XUUE,10260
|
|
8
|
-
spells/filter.py,sha256=J-YTOOAzOQpvIX29tviYL04RVoOUlfsbjBXoQBDCEdQ,3380
|
|
9
|
-
spells/manifest.py,sha256=XEJwwYJzOEWrQQwka39F9SgZo38hH_Kj4MPY7GOuLvY,6903
|
|
10
|
-
spells/schema.py,sha256=z8Qn2SiHG4T6YfPsz8xHLGMjU_Ofm76-Vrquh3b9B64,6422
|
|
11
|
-
spells_mtg-0.1.0.dist-info/METADATA,sha256=Q9FSV4QSWKXLtZCav1YCPTlgDGUXDNRzBO-BQQFIkFM,41050
|
|
12
|
-
spells_mtg-0.1.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
|
13
|
-
spells_mtg-0.1.0.dist-info/entry_points.txt,sha256=a9Y1omdl9MdnKuIj3aOodgrp-zZII6OCdvqwgP6BFvI,63
|
|
14
|
-
spells_mtg-0.1.0.dist-info/licenses/LICENSE,sha256=tS54XYbJSgmq5zuHhbsQGbNQLJPVgXqhF5nu2CSRMig,1068
|
|
15
|
-
spells_mtg-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|