spells-mtg 0.4.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of spells-mtg might be problematic. Click here for more details.
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/PKG-INFO +39 -11
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/README.md +38 -10
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/pyproject.toml +1 -1
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/columns.py +132 -127
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/draft_data.py +27 -12
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/enums.py +1 -0
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/manifest.py +4 -4
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/LICENSE +0 -0
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/__init__.py +0 -0
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/cache.py +0 -0
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/cards.py +0 -0
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/external.py +0 -0
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/filter.py +0 -0
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/spells/schema.py +0 -0
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/tests/__init__.py +0 -0
- {spells_mtg-0.4.0 → spells_mtg-0.5.0}/tests/filter_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: spells-mtg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: analaysis of 17Lands.com public datasets
|
|
5
5
|
Author-Email: Joel Barnes <oelarnes@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -74,8 +74,9 @@ 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 122 explicitly specified, enumerated, documented column definitions
|
|
78
78
|
- Supports "Deck Color Data" aggregations with built-in column definitions.
|
|
79
|
+
- Lets you feed card metrics back in to column definitions to support scientific workflows like MLE
|
|
79
80
|
- Provides a CLI tool `spells [add|refresh|clean|remove|info] [SET]` to download and manage external files
|
|
80
81
|
- Downloads and manages public datasets from 17Lands
|
|
81
82
|
- Retrieves and models booster configuration and card data from [MTGJSON](https://mtgjson.com/)
|
|
@@ -87,7 +88,7 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
87
88
|
|
|
88
89
|
## summon
|
|
89
90
|
|
|
90
|
-
`summon` takes
|
|
91
|
+
`summon` takes five optional arguments, allowing a fully declarative specification of your desired analysis. Basic functionality not provided by this api can often be managed by simple chained calls using the polars API, e.g. sorting and post-agg filtering.
|
|
91
92
|
- `columns` specifies the desired output columns
|
|
92
93
|
```python
|
|
93
94
|
>>> spells.summon('DSK', columns=["num_gp", "pct_gp", "gp_wr", "gp_wr_z"])
|
|
@@ -150,9 +151,9 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
150
151
|
- `extensions` allows for the specification of arbitrarily complex derived columns and aggregations, including custom columns built on top of custom columns.
|
|
151
152
|
```python
|
|
152
153
|
>>> import polars as pl
|
|
153
|
-
>>> from spells.columns import
|
|
154
|
+
>>> from spells.columns import ColSpec
|
|
154
155
|
>>> from spells.enums import ColType, View, ColName
|
|
155
|
-
>>> ext =
|
|
156
|
+
>>> ext = ColSpec(
|
|
156
157
|
... name='deq_base',
|
|
157
158
|
... col_type=ColType.AGG,
|
|
158
159
|
... expr=(pl.col('gp_wr_excess') + 0.03 * (1 - pl.col('ata')/14).pow(2)) * pl.col('pct_gp'),
|
|
@@ -180,8 +181,34 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
180
181
|
│ Oblivious Bookworm ┆ 0.028605 ┆ uncommon ┆ GU │
|
|
181
182
|
└──────────────────────────┴──────────┴──────────┴───────┘
|
|
182
183
|
```
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
- `card_context` takes a name-indexed DataFrame or name-keyed dict and allows the construction of column definitions based on the results.
|
|
185
|
+
```python
|
|
186
|
+
>>> deq = spells.summon('DSK', columns=['deq_base'], filter_spec={'player_cohort': 'Top'}, extensions=[ext])
|
|
187
|
+
>>> ext = [
|
|
188
|
+
... ColSpec(
|
|
189
|
+
... name='picked_deq_base',
|
|
190
|
+
... col_type=ColType.PICK_SUM,
|
|
191
|
+
... expr=lambda name, card_context: card_context[name]['deq_base']
|
|
192
|
+
... ),
|
|
193
|
+
... ColSpec(
|
|
194
|
+
... name='picked_deq_base_avg',
|
|
195
|
+
... col_type=ColType.AGG,
|
|
196
|
+
... expr=pl.col('picked_deq_base') / pl.col('num_taken')
|
|
197
|
+
... ),
|
|
198
|
+
... ]
|
|
199
|
+
>>> spells.summon('DSK', columns=['picked_deq_base_avg'], group_by=['player_cohort'], extensions=ext, card_context=deq)
|
|
200
|
+
shape: (4, 2)
|
|
201
|
+
┌───────────────┬─────────────────────┐
|
|
202
|
+
│ player_cohort ┆ picked_deq_base_avg │
|
|
203
|
+
│ --- ┆ --- │
|
|
204
|
+
│ str ┆ f64 │
|
|
205
|
+
╞═══════════════╪═════════════════════╡
|
|
206
|
+
│ Bottom ┆ 0.004826 │
|
|
207
|
+
│ Middle ┆ 0.00532 │
|
|
208
|
+
│ Other ┆ 0.004895 │
|
|
209
|
+
│ Top ┆ 0.005659 │
|
|
210
|
+
└───────────────┴─────────────────────┘
|
|
211
|
+
```
|
|
185
212
|
|
|
186
213
|
## Installation
|
|
187
214
|
|
|
@@ -289,7 +316,7 @@ aggregations of non-numeric (or numeric) data types are not supported. If `None`
|
|
|
289
316
|
- `{'lhs': 'player_cohort', 'op': 'in', 'rhs': ['Top', 'Middle']}` "player_cohort" value is either "Top" or "Middle". Supported values for `op` are `<`, `<=`, `>`, `>=`, `!=`, `=`, `in` and `nin`.
|
|
290
317
|
- `{'$and': [{'lhs': 'draft_date', 'op': '>', 'rhs': datetime.date(2024, 10, 7)}, {'rank': 'Mythic'}]}` Drafts after October 7 by Mythic-ranked players. Supported values for query construction keys are `$and`, `$or`, and `$not`.
|
|
291
318
|
|
|
292
|
-
- extensions: a list of `spells.columns.
|
|
319
|
+
- extensions: a list of `spells.columns.ColSpec` 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.
|
|
293
320
|
|
|
294
321
|
- 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.
|
|
295
322
|
|
|
@@ -301,12 +328,12 @@ from spells.enums import ColName, ColType
|
|
|
301
328
|
|
|
302
329
|
Recommended to import `ColName` for any usage of `summon`, and to import `ColType` when defining custom extensions.
|
|
303
330
|
|
|
304
|
-
###
|
|
331
|
+
### ColSpec
|
|
305
332
|
|
|
306
333
|
```python
|
|
307
|
-
from spells.columns import
|
|
334
|
+
from spells.columns import ColSpec
|
|
308
335
|
|
|
309
|
-
|
|
336
|
+
ColSpec(
|
|
310
337
|
name: str,
|
|
311
338
|
col_type: ColType,
|
|
312
339
|
expr: pl.Expr | Callable[..., pl.Expr] | None = None,
|
|
@@ -366,6 +393,7 @@ A table of all included columns. Columns can be referenced by enum or by string
|
|
|
366
393
|
| `PICK_NUMBER` | `"pick_number"` | `DRAFT` | `FILTER_ONLY` | Dataset Column | Int |
|
|
367
394
|
| `PICK_NUM` | `"pick_num"` | `DRAFT` | `GROUP_BY` | 1-indexed | Int |
|
|
368
395
|
| `TAKEN_AT` | `"taken_at` | `DRAFT` | `PICK_SUM` | Summable alias of `PICK_NUM` | Int |
|
|
396
|
+
| `NUM_DRAFTS` | `"num_drafts"` | `DRAFT` | `PICK_SUM` | | Int |
|
|
369
397
|
| `NUM_TAKEN` | `"num_taken"` | `DRAFT` | `PICK_SUM` | Sum 1 over rows | Int |
|
|
370
398
|
| `PICK` | `"pick"` | `DRAFT` | `FILTER_ONLY` | Dataset Column, joined as "name" | String |
|
|
371
399
|
| `PICK_MAINDECK_RATE` | `"pick_maindeck_rate"` | `DRAFT` | `PICK_SUM` | Dataset Column | Float |
|
|
@@ -63,8 +63,9 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
63
63
|
- Supports calculating the standard aggregations and measures out of the box with no arguments (ALSA, GIH WR, etc)
|
|
64
64
|
- Caches aggregate DataFrames in the local file system automatically for instantaneous reproduction of previous analysis
|
|
65
65
|
- Manages grouping and filtering by built-in and custom columns at the row level
|
|
66
|
-
- Provides
|
|
66
|
+
- Provides 122 explicitly specified, enumerated, documented column definitions
|
|
67
67
|
- Supports "Deck Color Data" aggregations with built-in column definitions.
|
|
68
|
+
- Lets you feed card metrics back in to column definitions to support scientific workflows like MLE
|
|
68
69
|
- Provides a CLI tool `spells [add|refresh|clean|remove|info] [SET]` to download and manage external files
|
|
69
70
|
- Downloads and manages public datasets from 17Lands
|
|
70
71
|
- Retrieves and models booster configuration and card data from [MTGJSON](https://mtgjson.com/)
|
|
@@ -76,7 +77,7 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
76
77
|
|
|
77
78
|
## summon
|
|
78
79
|
|
|
79
|
-
`summon` takes
|
|
80
|
+
`summon` takes five optional arguments, allowing a fully declarative specification of your desired analysis. Basic functionality not provided by this api can often be managed by simple chained calls using the polars API, e.g. sorting and post-agg filtering.
|
|
80
81
|
- `columns` specifies the desired output columns
|
|
81
82
|
```python
|
|
82
83
|
>>> spells.summon('DSK', columns=["num_gp", "pct_gp", "gp_wr", "gp_wr_z"])
|
|
@@ -139,9 +140,9 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
139
140
|
- `extensions` allows for the specification of arbitrarily complex derived columns and aggregations, including custom columns built on top of custom columns.
|
|
140
141
|
```python
|
|
141
142
|
>>> import polars as pl
|
|
142
|
-
>>> from spells.columns import
|
|
143
|
+
>>> from spells.columns import ColSpec
|
|
143
144
|
>>> from spells.enums import ColType, View, ColName
|
|
144
|
-
>>> ext =
|
|
145
|
+
>>> ext = ColSpec(
|
|
145
146
|
... name='deq_base',
|
|
146
147
|
... col_type=ColType.AGG,
|
|
147
148
|
... expr=(pl.col('gp_wr_excess') + 0.03 * (1 - pl.col('ata')/14).pow(2)) * pl.col('pct_gp'),
|
|
@@ -169,8 +170,34 @@ Spells is not affiliated with 17Lands. Please review the [Usage Guidelines](http
|
|
|
169
170
|
│ Oblivious Bookworm ┆ 0.028605 ┆ uncommon ┆ GU │
|
|
170
171
|
└──────────────────────────┴──────────┴──────────┴───────┘
|
|
171
172
|
```
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
- `card_context` takes a name-indexed DataFrame or name-keyed dict and allows the construction of column definitions based on the results.
|
|
174
|
+
```python
|
|
175
|
+
>>> deq = spells.summon('DSK', columns=['deq_base'], filter_spec={'player_cohort': 'Top'}, extensions=[ext])
|
|
176
|
+
>>> ext = [
|
|
177
|
+
... ColSpec(
|
|
178
|
+
... name='picked_deq_base',
|
|
179
|
+
... col_type=ColType.PICK_SUM,
|
|
180
|
+
... expr=lambda name, card_context: card_context[name]['deq_base']
|
|
181
|
+
... ),
|
|
182
|
+
... ColSpec(
|
|
183
|
+
... name='picked_deq_base_avg',
|
|
184
|
+
... col_type=ColType.AGG,
|
|
185
|
+
... expr=pl.col('picked_deq_base') / pl.col('num_taken')
|
|
186
|
+
... ),
|
|
187
|
+
... ]
|
|
188
|
+
>>> spells.summon('DSK', columns=['picked_deq_base_avg'], group_by=['player_cohort'], extensions=ext, card_context=deq)
|
|
189
|
+
shape: (4, 2)
|
|
190
|
+
┌───────────────┬─────────────────────┐
|
|
191
|
+
│ player_cohort ┆ picked_deq_base_avg │
|
|
192
|
+
│ --- ┆ --- │
|
|
193
|
+
│ str ┆ f64 │
|
|
194
|
+
╞═══════════════╪═════════════════════╡
|
|
195
|
+
│ Bottom ┆ 0.004826 │
|
|
196
|
+
│ Middle ┆ 0.00532 │
|
|
197
|
+
│ Other ┆ 0.004895 │
|
|
198
|
+
│ Top ┆ 0.005659 │
|
|
199
|
+
└───────────────┴─────────────────────┘
|
|
200
|
+
```
|
|
174
201
|
|
|
175
202
|
## Installation
|
|
176
203
|
|
|
@@ -278,7 +305,7 @@ aggregations of non-numeric (or numeric) data types are not supported. If `None`
|
|
|
278
305
|
- `{'lhs': 'player_cohort', 'op': 'in', 'rhs': ['Top', 'Middle']}` "player_cohort" value is either "Top" or "Middle". Supported values for `op` are `<`, `<=`, `>`, `>=`, `!=`, `=`, `in` and `nin`.
|
|
279
306
|
- `{'$and': [{'lhs': 'draft_date', 'op': '>', 'rhs': datetime.date(2024, 10, 7)}, {'rank': 'Mythic'}]}` Drafts after October 7 by Mythic-ranked players. Supported values for query construction keys are `$and`, `$or`, and `$not`.
|
|
280
307
|
|
|
281
|
-
- extensions: a list of `spells.columns.
|
|
308
|
+
- extensions: a list of `spells.columns.ColSpec` 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.
|
|
282
309
|
|
|
283
310
|
- 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.
|
|
284
311
|
|
|
@@ -290,12 +317,12 @@ from spells.enums import ColName, ColType
|
|
|
290
317
|
|
|
291
318
|
Recommended to import `ColName` for any usage of `summon`, and to import `ColType` when defining custom extensions.
|
|
292
319
|
|
|
293
|
-
###
|
|
320
|
+
### ColSpec
|
|
294
321
|
|
|
295
322
|
```python
|
|
296
|
-
from spells.columns import
|
|
323
|
+
from spells.columns import ColSpec
|
|
297
324
|
|
|
298
|
-
|
|
325
|
+
ColSpec(
|
|
299
326
|
name: str,
|
|
300
327
|
col_type: ColType,
|
|
301
328
|
expr: pl.Expr | Callable[..., pl.Expr] | None = None,
|
|
@@ -355,6 +382,7 @@ A table of all included columns. Columns can be referenced by enum or by string
|
|
|
355
382
|
| `PICK_NUMBER` | `"pick_number"` | `DRAFT` | `FILTER_ONLY` | Dataset Column | Int |
|
|
356
383
|
| `PICK_NUM` | `"pick_num"` | `DRAFT` | `GROUP_BY` | 1-indexed | Int |
|
|
357
384
|
| `TAKEN_AT` | `"taken_at` | `DRAFT` | `PICK_SUM` | Summable alias of `PICK_NUM` | Int |
|
|
385
|
+
| `NUM_DRAFTS` | `"num_drafts"` | `DRAFT` | `PICK_SUM` | | Int |
|
|
358
386
|
| `NUM_TAKEN` | `"num_taken"` | `DRAFT` | `PICK_SUM` | Sum 1 over rows | Int |
|
|
359
387
|
| `PICK` | `"pick"` | `DRAFT` | `FILTER_ONLY` | Dataset Column, joined as "name" | String |
|
|
360
388
|
| `PICK_MAINDECK_RATE` | `"pick_maindeck_rate"` | `DRAFT` | `PICK_SUM` | Dataset Column | Float |
|
|
@@ -7,7 +7,7 @@ from spells.enums import View, ColName, ColType
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@dataclass(frozen=True)
|
|
10
|
-
class
|
|
10
|
+
class ColSpec:
|
|
11
11
|
name: str
|
|
12
12
|
col_type: ColType
|
|
13
13
|
expr: pl.Expr | Callable[..., pl.Expr] | None = None
|
|
@@ -16,7 +16,7 @@ class ColumnSpec:
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@dataclass(frozen=True)
|
|
19
|
-
class
|
|
19
|
+
class ColDef:
|
|
20
20
|
name: str
|
|
21
21
|
col_type: ColType
|
|
22
22
|
expr: pl.Expr | tuple[pl.Expr, ...]
|
|
@@ -42,67 +42,67 @@ default_columns = [
|
|
|
42
42
|
]
|
|
43
43
|
|
|
44
44
|
_column_specs = [
|
|
45
|
-
|
|
45
|
+
ColSpec(
|
|
46
46
|
name=ColName.NAME,
|
|
47
47
|
col_type=ColType.GROUP_BY,
|
|
48
48
|
views=[View.CARD],
|
|
49
49
|
),
|
|
50
|
-
|
|
50
|
+
ColSpec(
|
|
51
51
|
name=ColName.EXPANSION,
|
|
52
52
|
col_type=ColType.GROUP_BY,
|
|
53
53
|
views=[View.GAME, View.DRAFT],
|
|
54
54
|
),
|
|
55
|
-
|
|
55
|
+
ColSpec(
|
|
56
56
|
name=ColName.EVENT_TYPE,
|
|
57
57
|
col_type=ColType.GROUP_BY,
|
|
58
58
|
views=[View.GAME, View.DRAFT],
|
|
59
59
|
),
|
|
60
|
-
|
|
60
|
+
ColSpec(
|
|
61
61
|
name=ColName.DRAFT_ID,
|
|
62
62
|
views=[View.GAME, View.DRAFT],
|
|
63
63
|
col_type=ColType.FILTER_ONLY,
|
|
64
64
|
),
|
|
65
|
-
|
|
65
|
+
ColSpec(
|
|
66
66
|
name=ColName.DRAFT_TIME,
|
|
67
67
|
col_type=ColType.FILTER_ONLY,
|
|
68
68
|
views=[View.GAME, View.DRAFT],
|
|
69
69
|
),
|
|
70
|
-
|
|
70
|
+
ColSpec(
|
|
71
71
|
name=ColName.DRAFT_DATE,
|
|
72
72
|
col_type=ColType.GROUP_BY,
|
|
73
73
|
expr=pl.col(ColName.DRAFT_TIME).str.to_datetime("%Y-%m-%d %H:%M:%S").dt.date(),
|
|
74
74
|
),
|
|
75
|
-
|
|
75
|
+
ColSpec(
|
|
76
76
|
name=ColName.DRAFT_DAY_OF_WEEK,
|
|
77
77
|
col_type=ColType.GROUP_BY,
|
|
78
78
|
expr=pl.col(ColName.DRAFT_TIME).str.to_datetime("%Y-%m-%d %H:%M:%S").dt.weekday(),
|
|
79
79
|
),
|
|
80
|
-
|
|
80
|
+
ColSpec(
|
|
81
81
|
name=ColName.DRAFT_HOUR,
|
|
82
82
|
col_type=ColType.GROUP_BY,
|
|
83
83
|
expr=pl.col(ColName.DRAFT_TIME).str.to_datetime("%Y-%m-%d %H:%M:%S").dt.hour(),
|
|
84
84
|
),
|
|
85
|
-
|
|
85
|
+
ColSpec(
|
|
86
86
|
name=ColName.DRAFT_WEEK,
|
|
87
87
|
col_type=ColType.GROUP_BY,
|
|
88
88
|
expr=pl.col(ColName.DRAFT_TIME).str.to_datetime("%Y-%m-%d %H:%M:%S").dt.week(),
|
|
89
89
|
),
|
|
90
|
-
|
|
90
|
+
ColSpec(
|
|
91
91
|
name=ColName.RANK,
|
|
92
92
|
col_type=ColType.GROUP_BY,
|
|
93
93
|
views=[View.GAME, View.DRAFT],
|
|
94
94
|
),
|
|
95
|
-
|
|
95
|
+
ColSpec(
|
|
96
96
|
name=ColName.USER_N_GAMES_BUCKET,
|
|
97
97
|
col_type=ColType.GROUP_BY,
|
|
98
98
|
views=[View.DRAFT, View.GAME],
|
|
99
99
|
),
|
|
100
|
-
|
|
100
|
+
ColSpec(
|
|
101
101
|
name=ColName.USER_GAME_WIN_RATE_BUCKET,
|
|
102
102
|
col_type=ColType.GROUP_BY,
|
|
103
103
|
views=[View.DRAFT, View.GAME],
|
|
104
104
|
),
|
|
105
|
-
|
|
105
|
+
ColSpec(
|
|
106
106
|
name=ColName.PLAYER_COHORT,
|
|
107
107
|
col_type=ColType.GROUP_BY,
|
|
108
108
|
expr=pl.when(pl.col(ColName.USER_N_GAMES_BUCKET) < 100)
|
|
@@ -117,303 +117,308 @@ _column_specs = [
|
|
|
117
117
|
)
|
|
118
118
|
),
|
|
119
119
|
),
|
|
120
|
-
|
|
120
|
+
ColSpec(
|
|
121
121
|
name=ColName.EVENT_MATCH_WINS,
|
|
122
122
|
col_type=ColType.GROUP_BY,
|
|
123
123
|
views=[View.DRAFT],
|
|
124
124
|
),
|
|
125
|
-
|
|
125
|
+
ColSpec(
|
|
126
126
|
name=ColName.EVENT_MATCH_WINS_SUM,
|
|
127
127
|
col_type=ColType.PICK_SUM,
|
|
128
128
|
views=[View.DRAFT],
|
|
129
129
|
expr=pl.col(ColName.EVENT_MATCH_WINS),
|
|
130
130
|
),
|
|
131
|
-
|
|
131
|
+
ColSpec(
|
|
132
132
|
name=ColName.EVENT_MATCH_LOSSES,
|
|
133
133
|
col_type=ColType.GROUP_BY,
|
|
134
134
|
views=[View.DRAFT],
|
|
135
135
|
),
|
|
136
|
-
|
|
136
|
+
ColSpec(
|
|
137
137
|
name=ColName.EVENT_MATCH_LOSSES_SUM,
|
|
138
138
|
col_type=ColType.PICK_SUM,
|
|
139
139
|
expr=pl.col(ColName.EVENT_MATCH_LOSSES),
|
|
140
140
|
),
|
|
141
|
-
|
|
141
|
+
ColSpec(
|
|
142
142
|
name=ColName.EVENT_MATCHES,
|
|
143
143
|
col_type=ColType.GROUP_BY,
|
|
144
144
|
expr=pl.col(ColName.EVENT_MATCH_WINS) + pl.col(ColName.EVENT_MATCH_LOSSES),
|
|
145
145
|
),
|
|
146
|
-
|
|
146
|
+
ColSpec(
|
|
147
147
|
name=ColName.EVENT_MATCHES_SUM,
|
|
148
148
|
col_type=ColType.PICK_SUM,
|
|
149
149
|
expr=pl.col(ColName.EVENT_MATCHES),
|
|
150
150
|
),
|
|
151
|
-
|
|
151
|
+
ColSpec(
|
|
152
152
|
name=ColName.IS_TROPHY,
|
|
153
153
|
col_type=ColType.GROUP_BY,
|
|
154
154
|
expr=pl.when(pl.col(ColName.EVENT_TYPE) == "Traditional")
|
|
155
155
|
.then(pl.col(ColName.EVENT_MATCH_WINS) == 3)
|
|
156
156
|
.otherwise(pl.col(ColName.EVENT_MATCH_WINS) == 7),
|
|
157
157
|
),
|
|
158
|
-
|
|
158
|
+
ColSpec(
|
|
159
159
|
name=ColName.IS_TROPHY_SUM,
|
|
160
160
|
col_type=ColType.PICK_SUM,
|
|
161
161
|
expr=pl.col(ColName.IS_TROPHY),
|
|
162
162
|
),
|
|
163
|
-
|
|
163
|
+
ColSpec(
|
|
164
164
|
name=ColName.PACK_NUMBER,
|
|
165
165
|
col_type=ColType.FILTER_ONLY, # use pack_num
|
|
166
166
|
views=[View.DRAFT],
|
|
167
167
|
),
|
|
168
|
-
|
|
168
|
+
ColSpec(
|
|
169
169
|
name=ColName.PACK_NUM,
|
|
170
170
|
col_type=ColType.GROUP_BY,
|
|
171
171
|
expr=pl.col(ColName.PACK_NUMBER) + 1,
|
|
172
172
|
),
|
|
173
|
-
|
|
173
|
+
ColSpec(
|
|
174
174
|
name=ColName.PICK_NUMBER,
|
|
175
175
|
col_type=ColType.FILTER_ONLY, # use pick_num
|
|
176
176
|
views=[View.DRAFT],
|
|
177
177
|
),
|
|
178
|
-
|
|
178
|
+
ColSpec(
|
|
179
179
|
name=ColName.PICK_NUM,
|
|
180
180
|
col_type=ColType.GROUP_BY,
|
|
181
181
|
expr=pl.col(ColName.PICK_NUMBER) + 1,
|
|
182
182
|
),
|
|
183
|
-
|
|
183
|
+
ColSpec(
|
|
184
184
|
name=ColName.TAKEN_AT,
|
|
185
185
|
col_type=ColType.PICK_SUM,
|
|
186
186
|
expr=pl.col(ColName.PICK_NUM),
|
|
187
187
|
),
|
|
188
|
-
|
|
188
|
+
ColSpec(
|
|
189
189
|
name=ColName.NUM_TAKEN,
|
|
190
190
|
col_type=ColType.PICK_SUM,
|
|
191
191
|
expr=pl.when(pl.col(ColName.PICK).is_not_null())
|
|
192
192
|
.then(1)
|
|
193
193
|
.otherwise(0),
|
|
194
194
|
),
|
|
195
|
-
|
|
195
|
+
ColSpec(
|
|
196
|
+
name=ColName.NUM_DRAFTS,
|
|
197
|
+
col_type=ColType.PICK_SUM,
|
|
198
|
+
expr=pl.when((pl.col(ColName.PACK_NUMBER) == 0) & (pl.col(ColName.PICK_NUMBER) == 0)).then(1).otherwise(0),
|
|
199
|
+
),
|
|
200
|
+
ColSpec(
|
|
196
201
|
name=ColName.PICK,
|
|
197
202
|
col_type=ColType.FILTER_ONLY,
|
|
198
203
|
views=[View.DRAFT],
|
|
199
204
|
),
|
|
200
|
-
|
|
205
|
+
ColSpec(
|
|
201
206
|
name=ColName.PICK_MAINDECK_RATE,
|
|
202
207
|
col_type=ColType.PICK_SUM,
|
|
203
208
|
views=[View.DRAFT],
|
|
204
209
|
),
|
|
205
|
-
|
|
210
|
+
ColSpec(
|
|
206
211
|
name=ColName.PICK_SIDEBOARD_IN_RATE,
|
|
207
212
|
col_type=ColType.PICK_SUM,
|
|
208
213
|
views=[View.DRAFT],
|
|
209
214
|
),
|
|
210
|
-
|
|
215
|
+
ColSpec(
|
|
211
216
|
name=ColName.PACK_CARD,
|
|
212
217
|
col_type=ColType.NAME_SUM,
|
|
213
218
|
views=[View.DRAFT],
|
|
214
219
|
),
|
|
215
|
-
|
|
220
|
+
ColSpec(
|
|
216
221
|
name=ColName.LAST_SEEN,
|
|
217
222
|
col_type=ColType.NAME_SUM,
|
|
218
223
|
expr=lambda name: pl.col(f"pack_card_{name}")
|
|
219
224
|
* pl.min_horizontal(ColName.PICK_NUM, 8),
|
|
220
225
|
),
|
|
221
|
-
|
|
226
|
+
ColSpec(
|
|
222
227
|
name=ColName.NUM_SEEN,
|
|
223
228
|
col_type=ColType.NAME_SUM,
|
|
224
229
|
expr=lambda name: pl.col(f"pack_card_{name}") * (pl.col(ColName.PICK_NUM) <= 8),
|
|
225
230
|
),
|
|
226
|
-
|
|
231
|
+
ColSpec(
|
|
227
232
|
name=ColName.POOL,
|
|
228
233
|
col_type=ColType.NAME_SUM,
|
|
229
234
|
views=[View.DRAFT],
|
|
230
235
|
),
|
|
231
|
-
|
|
236
|
+
ColSpec(
|
|
232
237
|
name=ColName.GAME_TIME,
|
|
233
238
|
col_type=ColType.FILTER_ONLY,
|
|
234
239
|
views=[View.GAME],
|
|
235
240
|
),
|
|
236
|
-
|
|
241
|
+
ColSpec(
|
|
237
242
|
name=ColName.GAME_DATE,
|
|
238
243
|
col_type=ColType.GROUP_BY,
|
|
239
244
|
expr=pl.col(ColName.GAME_TIME).str.to_datetime("%Y-%m-%d %H-%M-%S").dt.date(),
|
|
240
245
|
),
|
|
241
|
-
|
|
246
|
+
ColSpec(
|
|
242
247
|
name=ColName.GAME_DAY_OF_WEEK,
|
|
243
248
|
col_type=ColType.GROUP_BY,
|
|
244
249
|
expr=pl.col(ColName.GAME_TIME).str.to_datetime("%Y-%m-%d %H-%M-%S").dt.weekday(),
|
|
245
250
|
),
|
|
246
|
-
|
|
251
|
+
ColSpec(
|
|
247
252
|
name=ColName.GAME_HOUR,
|
|
248
253
|
col_type=ColType.GROUP_BY,
|
|
249
254
|
expr=pl.col(ColName.GAME_TIME).str.to_datetime("%Y-%m-%d %H-%M-%S").dt.hour(),
|
|
250
255
|
),
|
|
251
|
-
|
|
256
|
+
ColSpec(
|
|
252
257
|
name=ColName.GAME_WEEK,
|
|
253
258
|
col_type=ColType.GROUP_BY,
|
|
254
259
|
expr=pl.col(ColName.GAME_TIME).str.to_datetime("%Y-%m-%d %H-%M-%S").dt.week(),
|
|
255
260
|
),
|
|
256
|
-
|
|
261
|
+
ColSpec(
|
|
257
262
|
name=ColName.BUILD_INDEX,
|
|
258
263
|
col_type=ColType.GROUP_BY,
|
|
259
264
|
views=[View.GAME],
|
|
260
265
|
),
|
|
261
|
-
|
|
266
|
+
ColSpec(
|
|
262
267
|
name=ColName.MATCH_NUMBER,
|
|
263
268
|
col_type=ColType.GROUP_BY,
|
|
264
269
|
views=[View.GAME],
|
|
265
270
|
),
|
|
266
|
-
|
|
271
|
+
ColSpec(
|
|
267
272
|
name=ColName.GAME_NUMBER,
|
|
268
273
|
col_type=ColType.GROUP_BY,
|
|
269
274
|
views=[View.GAME],
|
|
270
275
|
),
|
|
271
|
-
|
|
276
|
+
ColSpec(
|
|
272
277
|
name=ColName.NUM_GAMES,
|
|
273
278
|
col_type=ColType.GAME_SUM,
|
|
274
279
|
expr=pl.col(ColName.GAME_NUMBER).is_not_null(),
|
|
275
280
|
),
|
|
276
|
-
|
|
281
|
+
ColSpec(
|
|
277
282
|
name=ColName.NUM_MATCHES,
|
|
278
283
|
col_type=ColType.GAME_SUM,
|
|
279
284
|
expr=pl.col(ColName.GAME_NUMBER) == 1,
|
|
280
285
|
),
|
|
281
|
-
|
|
286
|
+
ColSpec(
|
|
282
287
|
name=ColName.NUM_EVENTS,
|
|
283
288
|
col_type=ColType.GAME_SUM,
|
|
284
289
|
expr=(pl.col(ColName.GAME_NUMBER) == 1) & (pl.col(ColName.MATCH_NUMBER) == 1),
|
|
285
290
|
),
|
|
286
|
-
|
|
291
|
+
ColSpec(
|
|
287
292
|
name=ColName.OPP_RANK,
|
|
288
293
|
col_type=ColType.GROUP_BY,
|
|
289
294
|
views=[View.GAME],
|
|
290
295
|
),
|
|
291
|
-
|
|
296
|
+
ColSpec(
|
|
292
297
|
name=ColName.MAIN_COLORS,
|
|
293
298
|
col_type=ColType.GROUP_BY,
|
|
294
299
|
views=[View.GAME],
|
|
295
300
|
),
|
|
296
|
-
|
|
301
|
+
ColSpec(
|
|
297
302
|
name=ColName.NUM_COLORS,
|
|
298
303
|
col_type=ColType.GROUP_BY,
|
|
299
304
|
expr=pl.col(ColName.MAIN_COLORS).str.len_chars(),
|
|
300
305
|
),
|
|
301
|
-
|
|
306
|
+
ColSpec(
|
|
302
307
|
name=ColName.SPLASH_COLORS,
|
|
303
308
|
col_type=ColType.GROUP_BY,
|
|
304
309
|
views=[View.GAME],
|
|
305
310
|
),
|
|
306
|
-
|
|
311
|
+
ColSpec(
|
|
307
312
|
name=ColName.HAS_SPLASH,
|
|
308
313
|
col_type=ColType.GROUP_BY,
|
|
309
314
|
expr=pl.col(ColName.SPLASH_COLORS).str.len_chars() > 0,
|
|
310
315
|
),
|
|
311
|
-
|
|
316
|
+
ColSpec(
|
|
312
317
|
name=ColName.ON_PLAY,
|
|
313
318
|
col_type=ColType.GROUP_BY,
|
|
314
319
|
views=[View.GAME],
|
|
315
320
|
),
|
|
316
|
-
|
|
321
|
+
ColSpec(
|
|
317
322
|
name=ColName.NUM_ON_PLAY,
|
|
318
323
|
col_type=ColType.GAME_SUM,
|
|
319
324
|
expr=pl.col(ColName.ON_PLAY),
|
|
320
325
|
),
|
|
321
|
-
|
|
326
|
+
ColSpec(
|
|
322
327
|
name=ColName.NUM_MULLIGANS,
|
|
323
328
|
col_type=ColType.GROUP_BY,
|
|
324
329
|
views=[View.GAME],
|
|
325
330
|
),
|
|
326
|
-
|
|
331
|
+
ColSpec(
|
|
327
332
|
name=ColName.NUM_MULLIGANS_SUM,
|
|
328
333
|
col_type=ColType.GAME_SUM,
|
|
329
334
|
expr=pl.col(ColName.NUM_MULLIGANS),
|
|
330
335
|
),
|
|
331
|
-
|
|
336
|
+
ColSpec(
|
|
332
337
|
name=ColName.OPP_NUM_MULLIGANS,
|
|
333
338
|
col_type=ColType.GAME_SUM,
|
|
334
339
|
views=[View.GAME],
|
|
335
340
|
),
|
|
336
|
-
|
|
341
|
+
ColSpec(
|
|
337
342
|
name=ColName.OPP_NUM_MULLIGANS_SUM,
|
|
338
343
|
col_type=ColType.GAME_SUM,
|
|
339
344
|
expr=pl.col(ColName.OPP_NUM_MULLIGANS),
|
|
340
345
|
),
|
|
341
|
-
|
|
346
|
+
ColSpec(
|
|
342
347
|
name=ColName.OPP_COLORS,
|
|
343
348
|
col_type=ColType.GROUP_BY,
|
|
344
349
|
views=[View.GAME],
|
|
345
350
|
),
|
|
346
|
-
|
|
351
|
+
ColSpec(
|
|
347
352
|
name=ColName.NUM_TURNS,
|
|
348
353
|
col_type=ColType.GROUP_BY,
|
|
349
354
|
views=[View.GAME],
|
|
350
355
|
),
|
|
351
|
-
|
|
356
|
+
ColSpec(
|
|
352
357
|
name=ColName.NUM_TURNS_SUM,
|
|
353
358
|
col_type=ColType.GAME_SUM,
|
|
354
359
|
expr=pl.col(ColName.NUM_TURNS),
|
|
355
360
|
),
|
|
356
|
-
|
|
361
|
+
ColSpec(
|
|
357
362
|
name=ColName.WON,
|
|
358
363
|
col_type=ColType.GROUP_BY,
|
|
359
364
|
views=[View.GAME],
|
|
360
365
|
),
|
|
361
|
-
|
|
366
|
+
ColSpec(
|
|
362
367
|
name=ColName.NUM_WON,
|
|
363
368
|
col_type=ColType.GAME_SUM,
|
|
364
369
|
expr=pl.col(ColName.WON),
|
|
365
370
|
),
|
|
366
|
-
|
|
371
|
+
ColSpec(
|
|
367
372
|
name=ColName.OPENING_HAND,
|
|
368
373
|
col_type=ColType.NAME_SUM,
|
|
369
374
|
views=[View.GAME],
|
|
370
375
|
),
|
|
371
|
-
|
|
376
|
+
ColSpec(
|
|
372
377
|
name=ColName.WON_OPENING_HAND,
|
|
373
378
|
col_type=ColType.NAME_SUM,
|
|
374
379
|
expr=lambda name: pl.col(f"opening_hand_{name}") * pl.col(ColName.WON),
|
|
375
380
|
),
|
|
376
|
-
|
|
381
|
+
ColSpec(
|
|
377
382
|
name=ColName.DRAWN,
|
|
378
383
|
col_type=ColType.NAME_SUM,
|
|
379
384
|
views=[View.GAME],
|
|
380
385
|
),
|
|
381
|
-
|
|
386
|
+
ColSpec(
|
|
382
387
|
name=ColName.WON_DRAWN,
|
|
383
388
|
col_type=ColType.NAME_SUM,
|
|
384
389
|
expr=lambda name: pl.col(f"drawn_{name}") * pl.col(ColName.WON),
|
|
385
390
|
),
|
|
386
|
-
|
|
391
|
+
ColSpec(
|
|
387
392
|
name=ColName.TUTORED,
|
|
388
393
|
col_type=ColType.NAME_SUM,
|
|
389
394
|
views=[View.GAME],
|
|
390
395
|
),
|
|
391
|
-
|
|
396
|
+
ColSpec(
|
|
392
397
|
name=ColName.WON_TUTORED,
|
|
393
398
|
col_type=ColType.NAME_SUM,
|
|
394
399
|
expr=lambda name: pl.col(f"tutored_{name}") * pl.col(ColName.WON),
|
|
395
400
|
),
|
|
396
|
-
|
|
401
|
+
ColSpec(
|
|
397
402
|
name=ColName.DECK,
|
|
398
403
|
col_type=ColType.NAME_SUM,
|
|
399
404
|
views=[View.GAME],
|
|
400
405
|
),
|
|
401
|
-
|
|
406
|
+
ColSpec(
|
|
402
407
|
name=ColName.WON_DECK,
|
|
403
408
|
col_type=ColType.NAME_SUM,
|
|
404
409
|
expr=lambda name: pl.col(f"deck_{name}") * pl.col(ColName.WON),
|
|
405
410
|
),
|
|
406
|
-
|
|
411
|
+
ColSpec(
|
|
407
412
|
name=ColName.SIDEBOARD,
|
|
408
413
|
col_type=ColType.NAME_SUM,
|
|
409
414
|
views=[View.GAME],
|
|
410
415
|
),
|
|
411
|
-
|
|
416
|
+
ColSpec(
|
|
412
417
|
name=ColName.WON_SIDEBOARD,
|
|
413
418
|
col_type=ColType.NAME_SUM,
|
|
414
419
|
expr=lambda name: pl.col(f"sideboard_{name}") * pl.col(ColName.WON),
|
|
415
420
|
),
|
|
416
|
-
|
|
421
|
+
ColSpec(
|
|
417
422
|
name=ColName.NUM_GNS,
|
|
418
423
|
col_type=ColType.NAME_SUM,
|
|
419
424
|
expr=lambda name: pl.max_horizontal(
|
|
@@ -424,251 +429,251 @@ _column_specs = [
|
|
|
424
429
|
- pl.col(f"opening_hand_{name}"),
|
|
425
430
|
),
|
|
426
431
|
),
|
|
427
|
-
|
|
432
|
+
ColSpec(
|
|
428
433
|
name=ColName.WON_NUM_GNS,
|
|
429
434
|
col_type=ColType.NAME_SUM,
|
|
430
435
|
expr=lambda name: pl.col(ColName.WON) * pl.col(f"num_gns_{name}"),
|
|
431
436
|
),
|
|
432
|
-
|
|
437
|
+
ColSpec(
|
|
433
438
|
name=ColName.SET_CODE,
|
|
434
439
|
col_type=ColType.CARD_ATTR,
|
|
435
440
|
),
|
|
436
|
-
|
|
441
|
+
ColSpec(
|
|
437
442
|
name=ColName.COLOR,
|
|
438
443
|
col_type=ColType.CARD_ATTR,
|
|
439
444
|
),
|
|
440
|
-
|
|
445
|
+
ColSpec(
|
|
441
446
|
name=ColName.RARITY,
|
|
442
447
|
col_type=ColType.CARD_ATTR,
|
|
443
448
|
),
|
|
444
|
-
|
|
449
|
+
ColSpec(
|
|
445
450
|
name=ColName.COLOR_IDENTITY,
|
|
446
451
|
col_type=ColType.CARD_ATTR,
|
|
447
452
|
),
|
|
448
|
-
|
|
453
|
+
ColSpec(
|
|
449
454
|
name=ColName.CARD_TYPE,
|
|
450
455
|
col_type=ColType.CARD_ATTR,
|
|
451
456
|
),
|
|
452
|
-
|
|
457
|
+
ColSpec(
|
|
453
458
|
name=ColName.SUBTYPE,
|
|
454
459
|
col_type=ColType.CARD_ATTR,
|
|
455
460
|
),
|
|
456
|
-
|
|
461
|
+
ColSpec(
|
|
457
462
|
name=ColName.MANA_VALUE,
|
|
458
463
|
col_type=ColType.CARD_ATTR,
|
|
459
464
|
),
|
|
460
|
-
|
|
465
|
+
ColSpec(
|
|
461
466
|
name=ColName.DECK_MANA_VALUE,
|
|
462
467
|
col_type=ColType.NAME_SUM,
|
|
463
468
|
expr=lambda name, card_context: card_context[name][ColName.MANA_VALUE] * pl.col(f"deck_{name}"),
|
|
464
469
|
),
|
|
465
|
-
|
|
470
|
+
ColSpec(
|
|
466
471
|
name=ColName.DECK_LANDS,
|
|
467
472
|
col_type=ColType.NAME_SUM,
|
|
468
473
|
expr=lambda name, card_context: pl.col(f"deck_{name}") * ( 1 if 'Land' in card_context[name][ColName.CARD_TYPE] else 0 )
|
|
469
474
|
),
|
|
470
|
-
|
|
475
|
+
ColSpec(
|
|
471
476
|
name=ColName.DECK_SPELLS,
|
|
472
477
|
col_type=ColType.NAME_SUM,
|
|
473
478
|
expr=lambda name: pl.col(f"deck_{name}") - pl.col(f"deck_lands_{name}"),
|
|
474
479
|
),
|
|
475
|
-
|
|
480
|
+
ColSpec(
|
|
476
481
|
name=ColName.MANA_COST,
|
|
477
482
|
col_type=ColType.CARD_ATTR,
|
|
478
483
|
),
|
|
479
|
-
|
|
484
|
+
ColSpec(
|
|
480
485
|
name=ColName.POWER,
|
|
481
486
|
col_type=ColType.CARD_ATTR,
|
|
482
487
|
),
|
|
483
|
-
|
|
488
|
+
ColSpec(
|
|
484
489
|
name=ColName.TOUGHNESS,
|
|
485
490
|
col_type=ColType.CARD_ATTR,
|
|
486
491
|
),
|
|
487
|
-
|
|
492
|
+
ColSpec(
|
|
488
493
|
name=ColName.IS_BONUS_SHEET,
|
|
489
494
|
col_type=ColType.CARD_ATTR,
|
|
490
495
|
),
|
|
491
|
-
|
|
496
|
+
ColSpec(
|
|
492
497
|
name=ColName.IS_DFC,
|
|
493
498
|
col_type=ColType.CARD_ATTR,
|
|
494
499
|
),
|
|
495
|
-
|
|
500
|
+
ColSpec(
|
|
496
501
|
name=ColName.ORACLE_TEXT,
|
|
497
502
|
col_type=ColType.CARD_ATTR,
|
|
498
503
|
),
|
|
499
|
-
|
|
504
|
+
ColSpec(
|
|
500
505
|
name=ColName.CARD_JSON,
|
|
501
506
|
col_type=ColType.CARD_ATTR,
|
|
502
507
|
),
|
|
503
|
-
|
|
508
|
+
ColSpec(
|
|
504
509
|
name=ColName.PICKED_MATCH_WR,
|
|
505
510
|
col_type=ColType.AGG,
|
|
506
|
-
expr=pl.col(ColName.EVENT_MATCH_WINS_SUM) / pl.col(ColName.
|
|
511
|
+
expr=pl.col(ColName.EVENT_MATCH_WINS_SUM) / pl.col(ColName.EVENT_MATCHES_SUM),
|
|
507
512
|
),
|
|
508
|
-
|
|
513
|
+
ColSpec(
|
|
509
514
|
name=ColName.TROPHY_RATE,
|
|
510
515
|
col_type=ColType.AGG,
|
|
511
516
|
expr=pl.col(ColName.IS_TROPHY_SUM) / pl.col(ColName.NUM_TAKEN),
|
|
512
517
|
),
|
|
513
|
-
|
|
518
|
+
ColSpec(
|
|
514
519
|
name=ColName.GAME_WR,
|
|
515
520
|
col_type=ColType.AGG,
|
|
516
521
|
expr=pl.col(ColName.NUM_WON) / pl.col(ColName.NUM_GAMES),
|
|
517
522
|
),
|
|
518
|
-
|
|
523
|
+
ColSpec(
|
|
519
524
|
name=ColName.ALSA,
|
|
520
525
|
col_type=ColType.AGG,
|
|
521
526
|
expr=pl.col(ColName.LAST_SEEN) / pl.col(ColName.NUM_SEEN),
|
|
522
527
|
),
|
|
523
|
-
|
|
528
|
+
ColSpec(
|
|
524
529
|
name=ColName.ATA,
|
|
525
530
|
col_type=ColType.AGG,
|
|
526
531
|
expr=pl.col(ColName.TAKEN_AT) / pl.col(ColName.NUM_TAKEN),
|
|
527
532
|
),
|
|
528
|
-
|
|
533
|
+
ColSpec(
|
|
529
534
|
name=ColName.NUM_GP,
|
|
530
535
|
col_type=ColType.AGG,
|
|
531
536
|
expr=pl.col(ColName.DECK),
|
|
532
537
|
),
|
|
533
|
-
|
|
538
|
+
ColSpec(
|
|
534
539
|
name=ColName.PCT_GP,
|
|
535
540
|
col_type=ColType.AGG,
|
|
536
541
|
expr=pl.col(ColName.DECK) / (pl.col(ColName.DECK) + pl.col(ColName.SIDEBOARD)),
|
|
537
542
|
),
|
|
538
|
-
|
|
543
|
+
ColSpec(
|
|
539
544
|
name=ColName.GP_WR,
|
|
540
545
|
col_type=ColType.AGG,
|
|
541
546
|
expr=pl.col(ColName.WON_DECK) / pl.col(ColName.DECK),
|
|
542
547
|
),
|
|
543
|
-
|
|
548
|
+
ColSpec(
|
|
544
549
|
name=ColName.NUM_OH,
|
|
545
550
|
col_type=ColType.AGG,
|
|
546
551
|
expr=pl.col(ColName.OPENING_HAND),
|
|
547
552
|
),
|
|
548
|
-
|
|
553
|
+
ColSpec(
|
|
549
554
|
name=ColName.OH_WR,
|
|
550
555
|
col_type=ColType.AGG,
|
|
551
556
|
expr=pl.col(ColName.WON_OPENING_HAND) / pl.col(ColName.OPENING_HAND),
|
|
552
557
|
),
|
|
553
|
-
|
|
558
|
+
ColSpec(
|
|
554
559
|
name=ColName.NUM_GIH,
|
|
555
560
|
col_type=ColType.AGG,
|
|
556
561
|
expr=pl.col(ColName.OPENING_HAND) + pl.col(ColName.DRAWN),
|
|
557
562
|
),
|
|
558
|
-
|
|
563
|
+
ColSpec(
|
|
559
564
|
name=ColName.NUM_GIH_WON,
|
|
560
565
|
col_type=ColType.AGG,
|
|
561
566
|
expr=pl.col(ColName.WON_OPENING_HAND) + pl.col(ColName.WON_DRAWN),
|
|
562
567
|
),
|
|
563
|
-
|
|
568
|
+
ColSpec(
|
|
564
569
|
name=ColName.GIH_WR,
|
|
565
570
|
col_type=ColType.AGG,
|
|
566
571
|
expr=pl.col(ColName.NUM_GIH_WON) / pl.col(ColName.NUM_GIH),
|
|
567
572
|
),
|
|
568
|
-
|
|
573
|
+
ColSpec(
|
|
569
574
|
name=ColName.GNS_WR,
|
|
570
575
|
col_type=ColType.AGG,
|
|
571
576
|
expr=pl.col(ColName.WON_NUM_GNS) / pl.col(ColName.NUM_GNS),
|
|
572
577
|
),
|
|
573
|
-
|
|
578
|
+
ColSpec(
|
|
574
579
|
name=ColName.IWD,
|
|
575
580
|
col_type=ColType.AGG,
|
|
576
581
|
expr=pl.col(ColName.GIH_WR) - pl.col(ColName.GNS_WR),
|
|
577
582
|
),
|
|
578
|
-
|
|
583
|
+
ColSpec(
|
|
579
584
|
name=ColName.NUM_IN_POOL,
|
|
580
585
|
col_type=ColType.AGG,
|
|
581
586
|
expr=pl.col(ColName.DECK) + pl.col(ColName.SIDEBOARD),
|
|
582
587
|
),
|
|
583
|
-
|
|
588
|
+
ColSpec(
|
|
584
589
|
name=ColName.IN_POOL_WR,
|
|
585
590
|
col_type=ColType.AGG,
|
|
586
591
|
expr=(pl.col(ColName.WON_DECK) + pl.col(ColName.WON_SIDEBOARD))
|
|
587
592
|
/ pl.col(ColName.NUM_IN_POOL),
|
|
588
593
|
),
|
|
589
|
-
|
|
594
|
+
ColSpec(
|
|
590
595
|
name=ColName.DECK_TOTAL,
|
|
591
596
|
col_type=ColType.AGG,
|
|
592
597
|
expr=pl.col(ColName.DECK).sum(),
|
|
593
598
|
),
|
|
594
|
-
|
|
599
|
+
ColSpec(
|
|
595
600
|
name=ColName.WON_DECK_TOTAL,
|
|
596
601
|
col_type=ColType.AGG,
|
|
597
602
|
expr=pl.col(ColName.WON_DECK).sum(),
|
|
598
603
|
),
|
|
599
|
-
|
|
604
|
+
ColSpec(
|
|
600
605
|
name=ColName.GP_WR_MEAN,
|
|
601
606
|
col_type=ColType.AGG,
|
|
602
607
|
expr=pl.col(ColName.WON_DECK_TOTAL) / pl.col(ColName.DECK_TOTAL),
|
|
603
608
|
),
|
|
604
|
-
|
|
609
|
+
ColSpec(
|
|
605
610
|
name=ColName.GP_WR_EXCESS,
|
|
606
611
|
col_type=ColType.AGG,
|
|
607
612
|
expr=pl.col(ColName.GP_WR) - pl.col(ColName.GP_WR_MEAN),
|
|
608
613
|
),
|
|
609
|
-
|
|
614
|
+
ColSpec(
|
|
610
615
|
name=ColName.GP_WR_VAR,
|
|
611
616
|
col_type=ColType.AGG,
|
|
612
617
|
expr=(pl.col(ColName.GP_WR_EXCESS).pow(2) * pl.col(ColName.NUM_GP)).sum()
|
|
613
618
|
/ pl.col(ColName.DECK_TOTAL),
|
|
614
619
|
),
|
|
615
|
-
|
|
620
|
+
ColSpec(
|
|
616
621
|
name=ColName.GP_WR_STDEV,
|
|
617
622
|
col_type=ColType.AGG,
|
|
618
623
|
expr=pl.col(ColName.GP_WR_VAR).sqrt(),
|
|
619
624
|
),
|
|
620
|
-
|
|
625
|
+
ColSpec(
|
|
621
626
|
name=ColName.GP_WR_Z,
|
|
622
627
|
col_type=ColType.AGG,
|
|
623
628
|
expr=pl.col(ColName.GP_WR_EXCESS) / pl.col(ColName.GP_WR_STDEV),
|
|
624
629
|
),
|
|
625
|
-
|
|
630
|
+
ColSpec(
|
|
626
631
|
name=ColName.GIH_TOTAL,
|
|
627
632
|
col_type=ColType.AGG,
|
|
628
633
|
expr=pl.col(ColName.NUM_GIH).sum(),
|
|
629
634
|
),
|
|
630
|
-
|
|
635
|
+
ColSpec(
|
|
631
636
|
name=ColName.WON_GIH_TOTAL,
|
|
632
637
|
col_type=ColType.AGG,
|
|
633
638
|
expr=pl.col(ColName.NUM_GIH_WON).sum(),
|
|
634
639
|
),
|
|
635
|
-
|
|
640
|
+
ColSpec(
|
|
636
641
|
name=ColName.GIH_WR_MEAN,
|
|
637
642
|
col_type=ColType.AGG,
|
|
638
643
|
expr=pl.col(ColName.WON_GIH_TOTAL) / pl.col(ColName.GIH_TOTAL),
|
|
639
644
|
),
|
|
640
|
-
|
|
645
|
+
ColSpec(
|
|
641
646
|
name=ColName.GIH_WR_EXCESS,
|
|
642
647
|
col_type=ColType.AGG,
|
|
643
648
|
expr=pl.col(ColName.GIH_WR) - pl.col(ColName.GIH_WR_MEAN),
|
|
644
649
|
),
|
|
645
|
-
|
|
650
|
+
ColSpec(
|
|
646
651
|
name=ColName.GIH_WR_VAR,
|
|
647
652
|
col_type=ColType.AGG,
|
|
648
653
|
expr=(pl.col(ColName.GIH_WR_EXCESS).pow(2) * pl.col(ColName.NUM_GIH)).sum()
|
|
649
654
|
/ pl.col(ColName.GIH_TOTAL),
|
|
650
655
|
),
|
|
651
|
-
|
|
656
|
+
ColSpec(
|
|
652
657
|
name=ColName.GIH_WR_STDEV,
|
|
653
658
|
col_type=ColType.AGG,
|
|
654
659
|
expr=pl.col(ColName.GIH_WR_VAR).sqrt(),
|
|
655
660
|
),
|
|
656
|
-
|
|
661
|
+
ColSpec(
|
|
657
662
|
name=ColName.GIH_WR_Z,
|
|
658
663
|
col_type=ColType.AGG,
|
|
659
664
|
expr=pl.col(ColName.GIH_WR_EXCESS) / pl.col(ColName.GIH_WR_STDEV),
|
|
660
665
|
),
|
|
661
|
-
|
|
666
|
+
ColSpec(
|
|
662
667
|
name=ColName.DECK_MANA_VALUE_AVG,
|
|
663
668
|
col_type=ColType.AGG,
|
|
664
669
|
expr=pl.col(ColName.DECK_MANA_VALUE) / pl.col(ColName.DECK_SPELLS),
|
|
665
670
|
),
|
|
666
|
-
|
|
671
|
+
ColSpec(
|
|
667
672
|
name=ColName.DECK_LANDS_AVG,
|
|
668
673
|
col_type=ColType.AGG,
|
|
669
674
|
expr=pl.col(ColName.DECK_LANDS) / pl.col(ColName.NUM_GAMES),
|
|
670
675
|
),
|
|
671
|
-
|
|
676
|
+
ColSpec(
|
|
672
677
|
name=ColName.DECK_SPELLS_AVG,
|
|
673
678
|
col_type=ColType.AGG,
|
|
674
679
|
expr=pl.col(ColName.DECK_SPELLS) / pl.col(ColName.NUM_GAMES),
|
|
@@ -14,12 +14,13 @@ from inspect import signature
|
|
|
14
14
|
from typing import Callable, TypeVar, Any
|
|
15
15
|
|
|
16
16
|
import polars as pl
|
|
17
|
+
from polars.exceptions import ColumnNotFoundError
|
|
17
18
|
|
|
18
19
|
from spells.external import data_file_path
|
|
19
20
|
import spells.cache
|
|
20
21
|
import spells.filter
|
|
21
22
|
import spells.manifest
|
|
22
|
-
from spells.columns import
|
|
23
|
+
from spells.columns import ColDef, ColSpec
|
|
23
24
|
from spells.enums import View, ColName, ColType
|
|
24
25
|
|
|
25
26
|
|
|
@@ -53,7 +54,7 @@ def _get_names(set_code: str) -> list[str]:
|
|
|
53
54
|
return names
|
|
54
55
|
|
|
55
56
|
|
|
56
|
-
def _get_card_context(set_code: str, col_spec_map: dict[str,
|
|
57
|
+
def _get_card_context(set_code: str, col_spec_map: dict[str, ColSpec], card_context: pl.DataFrame | dict[str, dict[str, Any]] | None) -> dict[str, dict[str, Any]]:
|
|
57
58
|
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
59
|
col_def_map = _hydrate_col_defs(set_code, card_attr_specs, card_only=True)
|
|
59
60
|
|
|
@@ -65,12 +66,25 @@ def _get_card_context(set_code: str, col_spec_map: dict[str, ColumnSpec]) -> dic
|
|
|
65
66
|
card_df, frozenset(columns), col_def_map, is_agg_view=False
|
|
66
67
|
).to_dicts()
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
loaded_context = {row[ColName.NAME]: row for row in select_rows}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
if card_context is not None:
|
|
72
|
+
if isinstance(card_context, pl.DataFrame):
|
|
73
|
+
try:
|
|
74
|
+
card_context = {row[ColName.NAME]: row for row in card_context.to_dicts()}
|
|
75
|
+
except ColumnNotFoundError:
|
|
76
|
+
raise ValueError("card_context DataFrame must have column 'name'")
|
|
77
|
+
|
|
78
|
+
names = list(loaded_context.keys())
|
|
79
|
+
for name in names:
|
|
80
|
+
assert name in card_context, f"card_context must include a row for each card name. {name} missing."
|
|
81
|
+
for col, value in card_context[name].items():
|
|
82
|
+
loaded_context[name][col] = value
|
|
83
|
+
|
|
84
|
+
return loaded_context
|
|
71
85
|
|
|
72
86
|
|
|
73
|
-
def _determine_expression(spec:
|
|
87
|
+
def _determine_expression(spec: ColSpec, names: list[str], card_context: dict[str, dict]) -> pl.Expr | tuple[pl.Expr, ...]:
|
|
74
88
|
def seed_params(expr):
|
|
75
89
|
params = {}
|
|
76
90
|
|
|
@@ -115,7 +129,7 @@ def _determine_expression(spec: ColumnSpec, names: list[str], card_context: dict
|
|
|
115
129
|
return expr
|
|
116
130
|
|
|
117
131
|
|
|
118
|
-
def _infer_dependencies(name: str, expr: pl.Expr | tuple[pl.Expr,...], col_spec_map: dict[str,
|
|
132
|
+
def _infer_dependencies(name: str, expr: pl.Expr | tuple[pl.Expr,...], col_spec_map: dict[str, ColSpec], names: list[str]) -> set[str]:
|
|
119
133
|
dependencies = set()
|
|
120
134
|
tricky_ones = set()
|
|
121
135
|
|
|
@@ -150,13 +164,13 @@ def _infer_dependencies(name: str, expr: pl.Expr | tuple[pl.Expr,...], col_spec_
|
|
|
150
164
|
return dependencies
|
|
151
165
|
|
|
152
166
|
|
|
153
|
-
def _hydrate_col_defs(set_code: str, col_spec_map: dict[str,
|
|
167
|
+
def _hydrate_col_defs(set_code: str, col_spec_map: dict[str, ColSpec], card_context: pl.DataFrame | dict[str, dict] | None = None, card_only: bool =False):
|
|
154
168
|
names = _get_names(set_code)
|
|
155
169
|
|
|
156
170
|
if card_only:
|
|
157
171
|
card_context = {}
|
|
158
172
|
else:
|
|
159
|
-
card_context = _get_card_context(set_code, col_spec_map)
|
|
173
|
+
card_context = _get_card_context(set_code, col_spec_map, card_context)
|
|
160
174
|
|
|
161
175
|
assert len(names) > 0, "there should be names"
|
|
162
176
|
hydrated = {}
|
|
@@ -184,7 +198,7 @@ def _hydrate_col_defs(set_code: str, col_spec_map: dict[str, ColumnSpec], card_o
|
|
|
184
198
|
)
|
|
185
199
|
)
|
|
186
200
|
|
|
187
|
-
cdef =
|
|
201
|
+
cdef = ColDef(
|
|
188
202
|
name=spec.name,
|
|
189
203
|
col_type=spec.col_type,
|
|
190
204
|
views=set(spec.views or set()),
|
|
@@ -199,7 +213,7 @@ def _hydrate_col_defs(set_code: str, col_spec_map: dict[str, ColumnSpec], card_o
|
|
|
199
213
|
def _view_select(
|
|
200
214
|
df: DF,
|
|
201
215
|
view_cols: frozenset[str],
|
|
202
|
-
col_def_map: dict[str,
|
|
216
|
+
col_def_map: dict[str, ColDef],
|
|
203
217
|
is_agg_view: bool,
|
|
204
218
|
) -> DF:
|
|
205
219
|
base_cols = frozenset()
|
|
@@ -339,17 +353,18 @@ def summon(
|
|
|
339
353
|
columns: list[str] | None = None,
|
|
340
354
|
group_by: list[str] | None = None,
|
|
341
355
|
filter_spec: dict | None = None,
|
|
342
|
-
extensions: list[
|
|
356
|
+
extensions: list[ColSpec] | None = None,
|
|
343
357
|
use_streaming: bool = False,
|
|
344
358
|
read_cache: bool = True,
|
|
345
359
|
write_cache: bool = True,
|
|
360
|
+
card_context: pl.DataFrame | dict[str, dict] | None = None
|
|
346
361
|
) -> pl.DataFrame:
|
|
347
362
|
col_spec_map = dict(spells.columns.col_spec_map)
|
|
348
363
|
if extensions is not None:
|
|
349
364
|
for spec in extensions:
|
|
350
365
|
col_spec_map[spec.name] = spec
|
|
351
366
|
|
|
352
|
-
col_def_map = _hydrate_col_defs(set_code, col_spec_map)
|
|
367
|
+
col_def_map = _hydrate_col_defs(set_code, col_spec_map, card_context)
|
|
353
368
|
m = spells.manifest.create(col_def_map, columns, group_by, filter_spec)
|
|
354
369
|
|
|
355
370
|
calc_fn = functools.partial(_base_agg_df, set_code, m, use_streaming=use_streaming)
|
|
@@ -60,6 +60,7 @@ class ColName(StrEnum):
|
|
|
60
60
|
PICK_NUM = "pick_num" # pick_number plus 1
|
|
61
61
|
TAKEN_AT = "taken_at"
|
|
62
62
|
NUM_TAKEN = "num_taken"
|
|
63
|
+
NUM_DRAFTS = "num_drafts"
|
|
63
64
|
PICK = "pick"
|
|
64
65
|
PICK_MAINDECK_RATE = "pick_maindeck_rate"
|
|
65
66
|
PICK_SIDEBOARD_IN_RATE = "pick_sideboard_in_rate"
|
|
@@ -3,13 +3,13 @@ from dataclasses import dataclass
|
|
|
3
3
|
import spells.columns
|
|
4
4
|
import spells.filter
|
|
5
5
|
from spells.enums import View, ColName, ColType
|
|
6
|
-
from spells.columns import
|
|
6
|
+
from spells.columns import ColDef
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@dataclass(frozen=True)
|
|
10
10
|
class Manifest:
|
|
11
11
|
columns: tuple[str, ...]
|
|
12
|
-
col_def_map: dict[str,
|
|
12
|
+
col_def_map: dict[str, ColDef]
|
|
13
13
|
base_view_group_by: frozenset[str]
|
|
14
14
|
view_cols: dict[View, frozenset[str]]
|
|
15
15
|
group_by: tuple[str, ...]
|
|
@@ -85,7 +85,7 @@ class Manifest:
|
|
|
85
85
|
|
|
86
86
|
def _resolve_view_cols(
|
|
87
87
|
col_set: frozenset[str],
|
|
88
|
-
col_def_map: dict[str,
|
|
88
|
+
col_def_map: dict[str, ColDef],
|
|
89
89
|
) -> dict[View, frozenset[str]]:
|
|
90
90
|
"""
|
|
91
91
|
For each view ('game', 'draft', and 'card'), return the columns
|
|
@@ -153,7 +153,7 @@ def _resolve_view_cols(
|
|
|
153
153
|
|
|
154
154
|
|
|
155
155
|
def create(
|
|
156
|
-
col_def_map: dict[str,
|
|
156
|
+
col_def_map: dict[str, ColDef],
|
|
157
157
|
columns: list[str] | None = None,
|
|
158
158
|
group_by: list[str] | None = None,
|
|
159
159
|
filter_spec: dict | None = None,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|