easy-glm 0.1.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.
easy_glm-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Serban Dragne
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,336 @@
1
+ Metadata-Version: 2.4
2
+ Name: easy_glm
3
+ Version: 0.1.0
4
+ Summary: Automate insurance pricing with LASSO-regularised GLMs – blueprint generation, preprocessing, model fitting, rate table extraction & plotting. Built on glum.
5
+ Author-email: Serban Dragne <sadragne@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/serband/easy_glm
8
+ Project-URL: Repository, https://github.com/serband/easy_glm
9
+ Project-URL: Issues, https://github.com/serband/easy_glm/issues
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: <3.14,>=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: duckdb>=1.3.0
22
+ Requires-Dist: polars>=1.17.0
23
+ Requires-Dist: numpy>=1.24.0
24
+ Requires-Dist: pyarrow>=14.0.0
25
+ Requires-Dist: glum>=3.0.0
26
+ Requires-Dist: matplotlib>=3.7.0
27
+ Requires-Dist: seaborn>=0.12.0
28
+ Requires-Dist: scikit-learn>=1.3.0
29
+ Requires-Dist: pandas>=2.0.0
30
+ Requires-Dist: dask-ml>=1.0.0
31
+ Requires-Dist: numba>=0.61.0
32
+ Requires-Dist: llvmlite>=0.44.0
33
+ Requires-Dist: rdata>=0.9.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
37
+ Requires-Dist: black>=23.0.0; extra == "dev"
38
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
39
+ Requires-Dist: jupyter>=1.0.0; extra == "dev"
40
+ Requires-Dist: ipykernel>=6.0.0; extra == "dev"
41
+ Requires-Dist: build>=0.10.0; extra == "dev"
42
+ Requires-Dist: twine>=4.0.0; extra == "dev"
43
+ Dynamic: license-file
44
+
45
+ # easy_glm
46
+
47
+ Python package to automate building insurance ratetables using (fused) LASSO regularised GLMs. Internally it leverages [glum](https://glum.readthedocs.io/en/latest/) for fitting, providing a higher-level interface tailored to insurance pricing workflows (blueprints, preprocessing, model fitting, rate table extraction & plotting). Inspired by the R package [aglm](https://github.com/kkondo1981/aglm). Packaged with a modern `src/` layout.
48
+
49
+ ## Installation & Setup
50
+
51
+ This project uses `uv` for fast dependency management and `venv` for virtual environments to ensure reproducibility.
52
+
53
+ ### Prerequisites
54
+
55
+ 1. **Python 3.10–3.13** - CI tests these versions.
56
+ 2. **uv** - Fast Python package installer and resolver
57
+
58
+ Install `uv`:
59
+ ```bash
60
+ # On Unix/Linux/macOS
61
+ curl -LsSf https://astral.sh/uv/install.sh | sh
62
+
63
+ # On Windows (PowerShell)
64
+ powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
65
+ ```
66
+
67
+ ### Installing from Git (Single Command)
68
+
69
+ You can install the package directly from Git using a single command:
70
+
71
+ ```bash
72
+ uv pip install git+https://github.com/serband/easy_glm.git
73
+ ```
74
+
75
+ This is the fastest way to get started with easy_glm without cloning the repository.
76
+
77
+ ### Quick Setup
78
+
79
+ Choose one of the following methods to set up your development environment:
80
+
81
+ #### Option 1: Cross-platform Python script (Recommended)
82
+ ```bash
83
+ python setup_dev.py
84
+ ```
85
+
86
+ #### Option 2: Direct installation from Git
87
+ ```bash
88
+ uv pip install git+https://github.com/serband/easy_glm.git
89
+ ```
90
+
91
+ #### Option 3: Platform-specific scripts
92
+
93
+ **On Windows (PowerShell):**
94
+ ```powershell
95
+ .\setup_dev.ps1
96
+ ```
97
+
98
+ **On Unix/Linux/macOS:**
99
+ ```bash
100
+ chmod +x setup_dev.sh
101
+ ./setup_dev.sh
102
+ ```
103
+
104
+ ### Manual Setup
105
+
106
+ If you prefer to set up manually:
107
+
108
+ 1. **Create virtual environment:**
109
+ ```bash
110
+ python -m venv venv
111
+ ```
112
+
113
+ 2. **Activate virtual environment:**
114
+ ```bash
115
+ # On Windows
116
+ venv\Scripts\activate
117
+
118
+ # On Unix/Linux/macOS
119
+ source venv/bin/activate
120
+ ```
121
+
122
+ 3. **Install dependencies:**
123
+ ```bash
124
+ uv pip install -r requirements-dev.txt
125
+ uv pip install -e .
126
+ ```
127
+
128
+ ## Usage Example
129
+
130
+ Here's a complete example of how to use `easy_glm` to build and visualize insurance rate tables.
131
+
132
+ For a minimal runnable script, see `examples/basic_usage.py`.
133
+
134
+ ### 1. Import Libraries and Load Data
135
+
136
+ First, import the necessary libraries and load the sample dataset. The package includes a function to load a sample French motor insurance dataset.
137
+
138
+ ```python
139
+ import easy_glm
140
+ import polars as pl
141
+ import numpy as np
142
+
143
+ # Load the sample dataset
144
+ df = easy_glm.load_external_dataframe()
145
+
146
+ # Create a train-test split for validation
147
+ df = df.with_columns(
148
+ pl.when(pl.lit(np.random.rand(df.height) < 0.7))
149
+ .then(1)
150
+ .otherwise(0)
151
+ .alias("traintest")
152
+ )
153
+ ```
154
+
155
+ ### 2. Generate a Preprocessing Blueprint
156
+
157
+ The `generate_blueprint` function analyzes the dataframe and creates a "blueprint" that defines how each variable should be preprocessed for modeling.
158
+ - **Numeric columns**: It computes quantile breakpoints.
159
+ - **Categorical columns**: It identifies the levels to keep, lumping rare ones into an 'Other' category.
160
+
161
+ ```python
162
+ # Generate the blueprint for the dataset
163
+ blueprint = easy_glm.generate_blueprint(df)
164
+ ```
165
+
166
+ ### 3. Prepare Data for Modeling
167
+
168
+ Using the blueprint, the `prepare_data` function transforms the raw data into a feature matrix suitable for the GLM. It applies the transformations defined in the blueprint (binning for numerics, lumping for categoricals).
169
+
170
+ ```python
171
+ # Define predictor variables
172
+ predictor_variables = ['VehAge', 'Region', 'VehGas', 'DrivAge', 'BonusMalus', 'Density']
173
+
174
+ # Prepare the dataset for modelling
175
+ prepped_data = easy_glm.prepare_data(
176
+ df=df,
177
+ modelling_variables=predictor_variables,
178
+ additional_columns=['Exposure', 'ClaimNb'],
179
+ formats=blueprint,
180
+ traintest_column='traintest',
181
+ table_name='cars'
182
+ )
183
+ ```
184
+
185
+ ### 4. Fit the LASSO GLM
186
+
187
+ Fit a LASSO-regularized Generalized Linear Model (GLM) using the prepared data. The `fit_lasso_glm` function uses cross-validation to find the optimal regularization strength.
188
+
189
+ ```python
190
+ # Fit the model
191
+ model = easy_glm.fit_lasso_glm(
192
+ dataframe=prepped_data,
193
+ target="ClaimNb",
194
+ model_type="Poisson",
195
+ weight_col="Exposure",
196
+ train_test_col="traintest",
197
+ divide_target_by_weight=True
198
+ )
199
+ ```
200
+
201
+ ### 5. Predict on New Data (Optional)
202
+
203
+ If you have already prepared data (i.e. ran `prepare_data` with the same blueprint & predictors) you can obtain predictions using the helper:
204
+
205
+ ```python
206
+ # Assume `prepped_data` as above and `model` fitted
207
+ new_rows_prepped = prepped_data.head(10).select(pl.all().exclude(["ClaimNb", "Exposure", "traintest"]))
208
+ preds = easy_glm.predict_with_model(model, new_rows_prepped)
209
+ ```
210
+
211
+ If you start from raw rows, run `prepare_data` first with the same `formats` (blueprint) and predictor list.
212
+
213
+ ### 6. Generate All Rate Tables
214
+
215
+ With a fitted model, you can now generate the rate tables for all predictor variables. The `generate_all_ratetables` function loops through each variable and calculates its relativity.
216
+
217
+ ```python
218
+ # Generate rate tables for all predictor variables
219
+ all_tables = easy_glm.generate_all_ratetables(
220
+ model=model,
221
+ dataset=df,
222
+ predictor_variables=predictor_variables,
223
+ blueprint=blueprint
224
+ )
225
+
226
+ # You can access the rate table for a specific variable like this:
227
+ print(all_tables['VehAge'])
228
+ ```
229
+
230
+ ### 7. Plot the Rate Tables
231
+
232
+ Finally, visualize the relativities using the `plot_all_ratetables` function. This will generate a plot for each variable, making it easy to interpret the model's results.
233
+ - **Numeric variables** are shown as line plots.
234
+ - **Categorical variables** are shown as bar plots.
235
+
236
+ ```python
237
+ # Plot all rate tables
238
+ easy_glm.plot_all_ratetables(all_tables, blueprint)
239
+ ```
240
+
241
+ This will produce a series of plots, one for each variable.
242
+
243
+ ## Development
244
+
245
+ ### Activating the Environment
246
+
247
+ After initial setup, activate your environment:
248
+
249
+ ```bash
250
+ # On Windows
251
+ venv\Scripts\activate
252
+
253
+ # On Unix/Linux/macOS
254
+ source venv/bin/activate
255
+ ```
256
+
257
+ ### Code Quality
258
+
259
+ The project includes code quality tools:
260
+
261
+ ```bash
262
+ # Format code
263
+ black .
264
+
265
+ # Lint code
266
+ ruff check .
267
+
268
+ # Run tests
269
+ pytest
270
+ ```
271
+
272
+
273
+ ## Project Structure
274
+
275
+ ```
276
+ easy_glm/
277
+ ├── src/easy_glm/ # Library code (packaged)
278
+ │ └── core/ # Core implementation modules
279
+ ├── tests/ # Pytest test suite
280
+ ├── examples/ # Usage examples
281
+ ├── test.py # Lightweight smoke script
282
+ ├── pyproject.toml # Packaging configuration
283
+ ├── requirements*.txt # Dependency constraint files
284
+ ├── setup_dev.* # Dev environment helpers
285
+ └── README.md
286
+ ```
287
+
288
+ ## Dependencies
289
+
290
+ ### Core Dependencies
291
+ - **duckdb**: Fast analytical database for data processing (v1.3+)
292
+ - **polars**: Fast dataframes library (v1.17+)
293
+ - **numpy**: Numerical computing
294
+ - **pyarrow**: Columnar data format
295
+ - **glum**: GLM implementation (v3.0+)
296
+ - **pandas**: Data manipulation and analysis
297
+ - **matplotlib**: Plotting library
298
+ - **seaborn**: Statistical data visualization
299
+ - **scikit-learn**: Machine learning utilities
300
+
301
+ ### Development Dependencies
302
+ - **pytest**: Testing framework
303
+ - **black**: Code formatter
304
+ - **ruff**: Fast Python linter
305
+ - **jupyter**: Notebook environment
306
+
307
+ ## Additional Usage Ideas
308
+
309
+ Roadmap ideas:
310
+ * Export all ratetables to CSV / Parquet bundle
311
+ * Inverse transform scoring for new raw data (auto-prepare + predict)
312
+ * Automated monotonic binning / isotonic smoothing option
313
+ * CLI entry point (`python -m easy_glm build ...`)
314
+ * Optional caching of downloaded demo dataset
315
+ * Configurable blueprint strategies (equal-frequency vs fixed breaks)
316
+
317
+ ### Test Performance Tuning
318
+
319
+ CI sets `EASY_GLM_MAX_ROWS=500` to limit dataset size for quicker tests. You can mimic locally:
320
+ ```bash
321
+ export EASY_GLM_MAX_ROWS=500
322
+ pytest -q
323
+ ```
324
+
325
+ ## Contributing
326
+
327
+ See `CONTRIBUTING.md` for the full guide. Quick checklist:
328
+ ```bash
329
+ ruff check .
330
+ black .
331
+ pytest
332
+ ```
333
+
334
+ ## License
335
+
336
+ MIT – see `LICENSE`.
@@ -0,0 +1,292 @@
1
+ # easy_glm
2
+
3
+ Python package to automate building insurance ratetables using (fused) LASSO regularised GLMs. Internally it leverages [glum](https://glum.readthedocs.io/en/latest/) for fitting, providing a higher-level interface tailored to insurance pricing workflows (blueprints, preprocessing, model fitting, rate table extraction & plotting). Inspired by the R package [aglm](https://github.com/kkondo1981/aglm). Packaged with a modern `src/` layout.
4
+
5
+ ## Installation & Setup
6
+
7
+ This project uses `uv` for fast dependency management and `venv` for virtual environments to ensure reproducibility.
8
+
9
+ ### Prerequisites
10
+
11
+ 1. **Python 3.10–3.13** - CI tests these versions.
12
+ 2. **uv** - Fast Python package installer and resolver
13
+
14
+ Install `uv`:
15
+ ```bash
16
+ # On Unix/Linux/macOS
17
+ curl -LsSf https://astral.sh/uv/install.sh | sh
18
+
19
+ # On Windows (PowerShell)
20
+ powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
21
+ ```
22
+
23
+ ### Installing from Git (Single Command)
24
+
25
+ You can install the package directly from Git using a single command:
26
+
27
+ ```bash
28
+ uv pip install git+https://github.com/serband/easy_glm.git
29
+ ```
30
+
31
+ This is the fastest way to get started with easy_glm without cloning the repository.
32
+
33
+ ### Quick Setup
34
+
35
+ Choose one of the following methods to set up your development environment:
36
+
37
+ #### Option 1: Cross-platform Python script (Recommended)
38
+ ```bash
39
+ python setup_dev.py
40
+ ```
41
+
42
+ #### Option 2: Direct installation from Git
43
+ ```bash
44
+ uv pip install git+https://github.com/serband/easy_glm.git
45
+ ```
46
+
47
+ #### Option 3: Platform-specific scripts
48
+
49
+ **On Windows (PowerShell):**
50
+ ```powershell
51
+ .\setup_dev.ps1
52
+ ```
53
+
54
+ **On Unix/Linux/macOS:**
55
+ ```bash
56
+ chmod +x setup_dev.sh
57
+ ./setup_dev.sh
58
+ ```
59
+
60
+ ### Manual Setup
61
+
62
+ If you prefer to set up manually:
63
+
64
+ 1. **Create virtual environment:**
65
+ ```bash
66
+ python -m venv venv
67
+ ```
68
+
69
+ 2. **Activate virtual environment:**
70
+ ```bash
71
+ # On Windows
72
+ venv\Scripts\activate
73
+
74
+ # On Unix/Linux/macOS
75
+ source venv/bin/activate
76
+ ```
77
+
78
+ 3. **Install dependencies:**
79
+ ```bash
80
+ uv pip install -r requirements-dev.txt
81
+ uv pip install -e .
82
+ ```
83
+
84
+ ## Usage Example
85
+
86
+ Here's a complete example of how to use `easy_glm` to build and visualize insurance rate tables.
87
+
88
+ For a minimal runnable script, see `examples/basic_usage.py`.
89
+
90
+ ### 1. Import Libraries and Load Data
91
+
92
+ First, import the necessary libraries and load the sample dataset. The package includes a function to load a sample French motor insurance dataset.
93
+
94
+ ```python
95
+ import easy_glm
96
+ import polars as pl
97
+ import numpy as np
98
+
99
+ # Load the sample dataset
100
+ df = easy_glm.load_external_dataframe()
101
+
102
+ # Create a train-test split for validation
103
+ df = df.with_columns(
104
+ pl.when(pl.lit(np.random.rand(df.height) < 0.7))
105
+ .then(1)
106
+ .otherwise(0)
107
+ .alias("traintest")
108
+ )
109
+ ```
110
+
111
+ ### 2. Generate a Preprocessing Blueprint
112
+
113
+ The `generate_blueprint` function analyzes the dataframe and creates a "blueprint" that defines how each variable should be preprocessed for modeling.
114
+ - **Numeric columns**: It computes quantile breakpoints.
115
+ - **Categorical columns**: It identifies the levels to keep, lumping rare ones into an 'Other' category.
116
+
117
+ ```python
118
+ # Generate the blueprint for the dataset
119
+ blueprint = easy_glm.generate_blueprint(df)
120
+ ```
121
+
122
+ ### 3. Prepare Data for Modeling
123
+
124
+ Using the blueprint, the `prepare_data` function transforms the raw data into a feature matrix suitable for the GLM. It applies the transformations defined in the blueprint (binning for numerics, lumping for categoricals).
125
+
126
+ ```python
127
+ # Define predictor variables
128
+ predictor_variables = ['VehAge', 'Region', 'VehGas', 'DrivAge', 'BonusMalus', 'Density']
129
+
130
+ # Prepare the dataset for modelling
131
+ prepped_data = easy_glm.prepare_data(
132
+ df=df,
133
+ modelling_variables=predictor_variables,
134
+ additional_columns=['Exposure', 'ClaimNb'],
135
+ formats=blueprint,
136
+ traintest_column='traintest',
137
+ table_name='cars'
138
+ )
139
+ ```
140
+
141
+ ### 4. Fit the LASSO GLM
142
+
143
+ Fit a LASSO-regularized Generalized Linear Model (GLM) using the prepared data. The `fit_lasso_glm` function uses cross-validation to find the optimal regularization strength.
144
+
145
+ ```python
146
+ # Fit the model
147
+ model = easy_glm.fit_lasso_glm(
148
+ dataframe=prepped_data,
149
+ target="ClaimNb",
150
+ model_type="Poisson",
151
+ weight_col="Exposure",
152
+ train_test_col="traintest",
153
+ divide_target_by_weight=True
154
+ )
155
+ ```
156
+
157
+ ### 5. Predict on New Data (Optional)
158
+
159
+ If you have already prepared data (i.e. ran `prepare_data` with the same blueprint & predictors) you can obtain predictions using the helper:
160
+
161
+ ```python
162
+ # Assume `prepped_data` as above and `model` fitted
163
+ new_rows_prepped = prepped_data.head(10).select(pl.all().exclude(["ClaimNb", "Exposure", "traintest"]))
164
+ preds = easy_glm.predict_with_model(model, new_rows_prepped)
165
+ ```
166
+
167
+ If you start from raw rows, run `prepare_data` first with the same `formats` (blueprint) and predictor list.
168
+
169
+ ### 6. Generate All Rate Tables
170
+
171
+ With a fitted model, you can now generate the rate tables for all predictor variables. The `generate_all_ratetables` function loops through each variable and calculates its relativity.
172
+
173
+ ```python
174
+ # Generate rate tables for all predictor variables
175
+ all_tables = easy_glm.generate_all_ratetables(
176
+ model=model,
177
+ dataset=df,
178
+ predictor_variables=predictor_variables,
179
+ blueprint=blueprint
180
+ )
181
+
182
+ # You can access the rate table for a specific variable like this:
183
+ print(all_tables['VehAge'])
184
+ ```
185
+
186
+ ### 7. Plot the Rate Tables
187
+
188
+ Finally, visualize the relativities using the `plot_all_ratetables` function. This will generate a plot for each variable, making it easy to interpret the model's results.
189
+ - **Numeric variables** are shown as line plots.
190
+ - **Categorical variables** are shown as bar plots.
191
+
192
+ ```python
193
+ # Plot all rate tables
194
+ easy_glm.plot_all_ratetables(all_tables, blueprint)
195
+ ```
196
+
197
+ This will produce a series of plots, one for each variable.
198
+
199
+ ## Development
200
+
201
+ ### Activating the Environment
202
+
203
+ After initial setup, activate your environment:
204
+
205
+ ```bash
206
+ # On Windows
207
+ venv\Scripts\activate
208
+
209
+ # On Unix/Linux/macOS
210
+ source venv/bin/activate
211
+ ```
212
+
213
+ ### Code Quality
214
+
215
+ The project includes code quality tools:
216
+
217
+ ```bash
218
+ # Format code
219
+ black .
220
+
221
+ # Lint code
222
+ ruff check .
223
+
224
+ # Run tests
225
+ pytest
226
+ ```
227
+
228
+
229
+ ## Project Structure
230
+
231
+ ```
232
+ easy_glm/
233
+ ├── src/easy_glm/ # Library code (packaged)
234
+ │ └── core/ # Core implementation modules
235
+ ├── tests/ # Pytest test suite
236
+ ├── examples/ # Usage examples
237
+ ├── test.py # Lightweight smoke script
238
+ ├── pyproject.toml # Packaging configuration
239
+ ├── requirements*.txt # Dependency constraint files
240
+ ├── setup_dev.* # Dev environment helpers
241
+ └── README.md
242
+ ```
243
+
244
+ ## Dependencies
245
+
246
+ ### Core Dependencies
247
+ - **duckdb**: Fast analytical database for data processing (v1.3+)
248
+ - **polars**: Fast dataframes library (v1.17+)
249
+ - **numpy**: Numerical computing
250
+ - **pyarrow**: Columnar data format
251
+ - **glum**: GLM implementation (v3.0+)
252
+ - **pandas**: Data manipulation and analysis
253
+ - **matplotlib**: Plotting library
254
+ - **seaborn**: Statistical data visualization
255
+ - **scikit-learn**: Machine learning utilities
256
+
257
+ ### Development Dependencies
258
+ - **pytest**: Testing framework
259
+ - **black**: Code formatter
260
+ - **ruff**: Fast Python linter
261
+ - **jupyter**: Notebook environment
262
+
263
+ ## Additional Usage Ideas
264
+
265
+ Roadmap ideas:
266
+ * Export all ratetables to CSV / Parquet bundle
267
+ * Inverse transform scoring for new raw data (auto-prepare + predict)
268
+ * Automated monotonic binning / isotonic smoothing option
269
+ * CLI entry point (`python -m easy_glm build ...`)
270
+ * Optional caching of downloaded demo dataset
271
+ * Configurable blueprint strategies (equal-frequency vs fixed breaks)
272
+
273
+ ### Test Performance Tuning
274
+
275
+ CI sets `EASY_GLM_MAX_ROWS=500` to limit dataset size for quicker tests. You can mimic locally:
276
+ ```bash
277
+ export EASY_GLM_MAX_ROWS=500
278
+ pytest -q
279
+ ```
280
+
281
+ ## Contributing
282
+
283
+ See `CONTRIBUTING.md` for the full guide. Quick checklist:
284
+ ```bash
285
+ ruff check .
286
+ black .
287
+ pytest
288
+ ```
289
+
290
+ ## License
291
+
292
+ MIT – see `LICENSE`.