typola 0.1.1__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.
Files changed (75) hide show
  1. typola-0.1.1/.github/workflows/ci.yml +207 -0
  2. typola-0.1.1/.gitignore +91 -0
  3. typola-0.1.1/LICENSE +21 -0
  4. typola-0.1.1/PKG-INFO +208 -0
  5. typola-0.1.1/README.md +175 -0
  6. typola-0.1.1/app.toml +22 -0
  7. typola-0.1.1/misc/example_bakeoff.py +144 -0
  8. typola-0.1.1/pyproject.toml +53 -0
  9. typola-0.1.1/tests/conftest.py +96 -0
  10. typola-0.1.1/tests/test_api.py +130 -0
  11. typola-0.1.1/tests/test_estimators.py +176 -0
  12. typola-0.1.1/tests/test_models.py +134 -0
  13. typola-0.1.1/tests/test_parameter_conditions.py +75 -0
  14. typola-0.1.1/tests/test_prep.py +91 -0
  15. typola-0.1.1/tests/test_query.py +134 -0
  16. typola-0.1.1/tests/test_stores.py +44 -0
  17. typola-0.1.1/typola/__init__.py +49 -0
  18. typola-0.1.1/typola/data_dir.py +39 -0
  19. typola-0.1.1/typola/estimators/__init__.py +53 -0
  20. typola-0.1.1/typola/estimators/base.py +149 -0
  21. typola-0.1.1/typola/estimators/smoothing.py +264 -0
  22. typola-0.1.1/typola/models/__init__.py +18 -0
  23. typola-0.1.1/typola/models/conditional.py +131 -0
  24. typola-0.1.1/typola/models/distribution.py +123 -0
  25. typola-0.1.1/typola/models/marginal.py +85 -0
  26. typola-0.1.1/typola/prep/__init__.py +22 -0
  27. typola-0.1.1/typola/prep/canonical.py +276 -0
  28. typola-0.1.1/typola/prep/cldf.py +97 -0
  29. typola-0.1.1/typola/prep/loaders.py +67 -0
  30. typola-0.1.1/typola/prep/stores.py +101 -0
  31. typola-0.1.1/typola/query/__init__.py +17 -0
  32. typola-0.1.1/typola/query/api.py +278 -0
  33. typola-0.1.1/typola/sources/__init__.py +20 -0
  34. typola-0.1.1/typola/sources/base.py +162 -0
  35. typola-0.1.1/typola/sources/catalog.py +64 -0
  36. typola-0.1.1/webapp/README.md +143 -0
  37. typola-0.1.1/webapp/api/__init__.py +1 -0
  38. typola-0.1.1/webapp/api/deps.py +50 -0
  39. typola-0.1.1/webapp/api/main.py +462 -0
  40. typola-0.1.1/webapp/api/schemas.py +222 -0
  41. typola-0.1.1/webapp/ui/.gitignore +4 -0
  42. typola-0.1.1/webapp/ui/index.html +12 -0
  43. typola-0.1.1/webapp/ui/package-lock.json +4472 -0
  44. typola-0.1.1/webapp/ui/package.json +53 -0
  45. typola-0.1.1/webapp/ui/postcss.config.js +3 -0
  46. typola-0.1.1/webapp/ui/src/App.tsx +228 -0
  47. typola-0.1.1/webapp/ui/src/components/CPTView.tsx +116 -0
  48. typola-0.1.1/webapp/ui/src/components/CodePicker.tsx +105 -0
  49. typola-0.1.1/webapp/ui/src/components/DistributionView.tsx +77 -0
  50. typola-0.1.1/webapp/ui/src/components/EstimatorPicker.tsx +96 -0
  51. typola-0.1.1/webapp/ui/src/components/ExamplesStrip.tsx +82 -0
  52. typola-0.1.1/webapp/ui/src/components/FilterChips.tsx +286 -0
  53. typola-0.1.1/webapp/ui/src/components/HistorySidebar.tsx +192 -0
  54. typola-0.1.1/webapp/ui/src/components/ParameterPicker.tsx +145 -0
  55. typola-0.1.1/webapp/ui/src/components/QueryBuilder.tsx +189 -0
  56. typola-0.1.1/webapp/ui/src/components/ResultCard.tsx +464 -0
  57. typola-0.1.1/webapp/ui/src/components/TypologyPicker.tsx +64 -0
  58. typola-0.1.1/webapp/ui/src/components/ui/badge.tsx +31 -0
  59. typola-0.1.1/webapp/ui/src/components/ui/button.tsx +50 -0
  60. typola-0.1.1/webapp/ui/src/components/ui/card.tsx +65 -0
  61. typola-0.1.1/webapp/ui/src/components/ui/command.tsx +106 -0
  62. typola-0.1.1/webapp/ui/src/components/ui/dialog.tsx +75 -0
  63. typola-0.1.1/webapp/ui/src/components/ui/popover.tsx +26 -0
  64. typola-0.1.1/webapp/ui/src/components/ui/scroll-area.tsx +41 -0
  65. typola-0.1.1/webapp/ui/src/components/ui/select.tsx +73 -0
  66. typola-0.1.1/webapp/ui/src/components/ui/separator.tsx +21 -0
  67. typola-0.1.1/webapp/ui/src/components/ui/tooltip.tsx +23 -0
  68. typola-0.1.1/webapp/ui/src/main.tsx +21 -0
  69. typola-0.1.1/webapp/ui/src/stores/historyCollection.ts +122 -0
  70. typola-0.1.1/webapp/ui/src/stores/session.ts +90 -0
  71. typola-0.1.1/webapp/ui/src/styles/globals.css +79 -0
  72. typola-0.1.1/webapp/ui/src/vite-env.d.ts +9 -0
  73. typola-0.1.1/webapp/ui/tailwind.config.js +68 -0
  74. typola-0.1.1/webapp/ui/tsconfig.json +25 -0
  75. typola-0.1.1/webapp/ui/vite.config.ts +30 -0
@@ -0,0 +1,207 @@
1
+ name: Continuous Integration (uv)
2
+ on: [push, pull_request]
3
+
4
+ # Note: Environment variables (PROJECT_NAME and vars from [tool.wads.ci.env])
5
+ # are set by the read-ci-config action in the setup job and made available
6
+ # to all subsequent jobs via GITHUB_ENV
7
+
8
+ jobs:
9
+ # First job: Read configuration from pyproject.toml
10
+ setup:
11
+ name: Read Configuration
12
+ runs-on: ubuntu-latest
13
+ outputs:
14
+ project-name: ${{ steps.config.outputs.project-name }}
15
+ python-versions: ${{ steps.config.outputs.python-versions }}
16
+ pytest-args: ${{ steps.config.outputs.pytest-args }}
17
+ coverage-enabled: ${{ steps.config.outputs.coverage-enabled }}
18
+ exclude-paths: ${{ steps.config.outputs.exclude-paths }}
19
+ test-on-windows: ${{ steps.config.outputs.test-on-windows }}
20
+ build-sdist: ${{ steps.config.outputs.build-sdist }}
21
+ build-wheel: ${{ steps.config.outputs.build-wheel }}
22
+ metrics-enabled: ${{ steps.config.outputs.metrics-enabled }}
23
+ metrics-config-path: ${{ steps.config.outputs.metrics-config-path }}
24
+ metrics-storage-branch: ${{ steps.config.outputs.metrics-storage-branch }}
25
+ metrics-python-version: ${{ steps.config.outputs.metrics-python-version }}
26
+ metrics-force-run: ${{ steps.config.outputs.metrics-force-run }}
27
+
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+
31
+ - name: Set up uv
32
+ uses: astral-sh/setup-uv@v5
33
+
34
+ - name: Set up Python
35
+ run: uv python install 3.11
36
+
37
+ - name: Read CI Config
38
+ id: config
39
+ uses: i2mint/wads/actions/read-ci-config@master
40
+ with:
41
+ pyproject-path: .
42
+
43
+ # Second job: Validation using the config
44
+ validation:
45
+ name: Validation
46
+ if: "!contains(github.event.head_commit.message, '[skip ci]')"
47
+ needs: setup
48
+ runs-on: ubuntu-latest
49
+ strategy:
50
+ matrix:
51
+ python-version: ${{ fromJson(needs.setup.outputs.python-versions) }}
52
+
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+
56
+ - name: Set up uv
57
+ uses: astral-sh/setup-uv@v5
58
+ with:
59
+ enable-cache: true
60
+
61
+ - name: Set up Python ${{ matrix.python-version }}
62
+ uses: i2mint/wads/actions/setup-python-uv@master
63
+ with:
64
+ python-version: ${{ matrix.python-version }}
65
+
66
+ - name: Install System Dependencies
67
+ uses: i2mint/wads/actions/install-system-deps@master
68
+ with:
69
+ pyproject-path: .
70
+
71
+ - name: Install Dependencies
72
+ uses: i2mint/wads/actions/install-deps-uv@master
73
+
74
+ - name: Format Source Code
75
+ run: uvx ruff format .
76
+
77
+ - name: Lint Validation
78
+ run: uvx ruff check --output-format=github ${{ needs.setup.outputs.project-name }}
79
+
80
+ - name: Run Tests
81
+ uses: i2mint/wads/actions/run-tests-uv@master
82
+ with:
83
+ root-dir: ${{ needs.setup.outputs.project-name }}
84
+ pytest-args: ${{ needs.setup.outputs.pytest-args }}
85
+ exclude-paths: ${{ needs.setup.outputs.exclude-paths }}
86
+ coverage: ${{ needs.setup.outputs.coverage-enabled }}
87
+
88
+ - name: Track Code Metrics
89
+ if: needs.setup.outputs.metrics-enabled == 'true'
90
+ uses: i2mint/umpyre/actions/track-metrics@master
91
+ continue-on-error: true
92
+ with:
93
+ github-token: ${{ secrets.GITHUB_TOKEN }}
94
+ config-path: ${{ needs.setup.outputs.metrics-config-path }}
95
+ storage-branch: ${{ needs.setup.outputs.metrics-storage-branch }}
96
+ python-version: ${{ needs.setup.outputs.metrics-python-version }}
97
+ force-run: ${{ needs.setup.outputs.metrics-force-run }}
98
+
99
+ # Optional Windows testing (if enabled in config)
100
+ windows-validation:
101
+ name: Windows Tests
102
+ if: "!contains(github.event.head_commit.message, '[skip ci]') && needs.setup.outputs.test-on-windows == 'true'"
103
+ needs: setup
104
+ runs-on: windows-latest
105
+ continue-on-error: true
106
+
107
+ steps:
108
+ - uses: actions/checkout@v4
109
+
110
+ - name: Set up uv
111
+ uses: astral-sh/setup-uv@v5
112
+ with:
113
+ enable-cache: true
114
+
115
+ - name: Set up Python
116
+ uses: i2mint/wads/actions/setup-python-uv@master
117
+ with:
118
+ python-version: ${{ fromJson(needs.setup.outputs.python-versions)[0] }}
119
+
120
+ - name: Install System Dependencies
121
+ uses: i2mint/wads/actions/install-system-deps@master
122
+ with:
123
+ pyproject-path: .
124
+
125
+ - name: Install Dependencies
126
+ uses: i2mint/wads/actions/install-deps-uv@master
127
+
128
+ - name: Run Tests
129
+ uses: i2mint/wads/actions/run-tests-uv@master
130
+ with:
131
+ root-dir: ${{ needs.setup.outputs.project-name }}
132
+
133
+ # Publishing job
134
+ publish:
135
+ name: Publish
136
+ permissions:
137
+ contents: write
138
+ if: "!contains(github.event.head_commit.message, '[skip ci]') && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main')"
139
+ needs: [setup, validation]
140
+ runs-on: ubuntu-latest
141
+
142
+ steps:
143
+ - uses: actions/checkout@v4
144
+ with:
145
+ fetch-depth: 0
146
+ token: ${{ secrets.GITHUB_TOKEN }}
147
+
148
+ - name: Set up uv
149
+ uses: astral-sh/setup-uv@v5
150
+
151
+ - name: Set up Python
152
+ uses: i2mint/wads/actions/setup-python-uv@master
153
+ with:
154
+ python-version: ${{ fromJson(needs.setup.outputs.python-versions)[0] }}
155
+ create-venv: "false"
156
+
157
+ - name: Format Source Code
158
+ run: uvx ruff format .
159
+
160
+ - name: Update Version Number
161
+ id: version
162
+ uses: i2mint/isee/actions/bump-version-number@master
163
+
164
+ - name: Build Distribution
165
+ uses: i2mint/wads/actions/build-dist-uv@master
166
+ with:
167
+ sdist: ${{ needs.setup.outputs.build-sdist }}
168
+ wheel: ${{ needs.setup.outputs.build-wheel }}
169
+
170
+ - name: Publish to PyPI
171
+ uses: i2mint/wads/actions/pypi-publish-uv@master
172
+ with:
173
+ pypi-token: ${{ secrets.PYPI_PASSWORD }}
174
+
175
+ - name: Force SSH for git remote
176
+ run: git remote set-url origin git@github.com:${{ github.repository }}.git
177
+
178
+ - name: Commit Changes
179
+ uses: i2mint/wads/actions/git-commit@master
180
+ with:
181
+ commit-message: "**CI** Formatted code + Updated version to ${{ env.VERSION }} [skip ci]"
182
+ ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
183
+ push: true
184
+
185
+ - name: Tag Repository
186
+ uses: i2mint/wads/actions/git-tag@master
187
+ with:
188
+ tag: ${{ env.VERSION }}
189
+ message: "Release version ${{ env.VERSION }}"
190
+ push: true
191
+
192
+ # Optional GitHub Pages
193
+ github-pages:
194
+ name: Publish GitHub Pages
195
+ permissions:
196
+ contents: write
197
+ pages: write
198
+ id-token: write
199
+ if: "!contains(github.event.head_commit.message, '[skip ci]') && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)"
200
+ needs: publish
201
+ runs-on: ubuntu-latest
202
+
203
+ steps:
204
+ - uses: i2mint/epythet/actions/publish-github-pages@master
205
+ with:
206
+ github-token: ${{ secrets.GITHUB_TOKEN }}
207
+ ignore: "tests/,scrap/,examples/"
@@ -0,0 +1,91 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ .DS_Store
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # TLS certificates
12
+ *.pem
13
+ certs/
14
+
15
+ # Distribution / packaging
16
+ .Python
17
+ build/
18
+ develop-eggs/
19
+ dist/
20
+ downloads/
21
+ eggs/
22
+ .eggs/
23
+ lib/
24
+ lib64/
25
+ parts/
26
+ sdist/
27
+ var/
28
+ wheels/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+ MANIFEST
33
+ _build
34
+
35
+ # PyInstaller
36
+ *.manifest
37
+ *.spec
38
+
39
+ # Installer logs
40
+ pip-log.txt
41
+ pip-delete-this-directory.txt
42
+
43
+ # Unit test / coverage reports
44
+ htmlcov/
45
+ .tox/
46
+ .coverage
47
+ .coverage.*
48
+ .cache
49
+ nosetests.xml
50
+ coverage.xml
51
+ *.cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Sphinx documentation
60
+ docs/_build/
61
+
62
+ # Jupyter Notebook
63
+ .ipynb_checkpoints
64
+
65
+ # pyenv
66
+ .python-version
67
+
68
+ # Environments
69
+ .env
70
+ .venv
71
+ env/
72
+ venv/
73
+ ENV/
74
+ env.bak/
75
+ venv.bak/
76
+
77
+ # Editors
78
+ .vscode/
79
+ .idea/
80
+ .spyderproject
81
+ .spyproject
82
+ .ropeproject
83
+
84
+ # mypy
85
+ .mypy_cache/
86
+
87
+ # Node / frontend
88
+ node_modules/
89
+
90
+ # typola data cache (downloaded CLDF datasets)
91
+ data_cache/
typola-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thor Whalen
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.
typola-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: typola
3
+ Version: 0.1.1
4
+ Summary: Probabilistic models over linguistic typology source data (WALS, Grambank, ...) with pluggable count-to-probability estimators.
5
+ Project-URL: Homepage, https://github.com/thorwhalen/typola
6
+ Project-URL: Repository, https://github.com/thorwhalen/typola
7
+ Author: Thor Whalen
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: bayesian,cldf,grambank,linguistics,probabilistic-models,typology,wals
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: dol>=0.3
13
+ Requires-Dist: numpy>=1.23
14
+ Requires-Dist: pandas>=1.5
15
+ Requires-Dist: requests>=2.28
16
+ Requires-Dist: scipy>=1.10
17
+ Provides-Extra: bayesian
18
+ Requires-Dist: ba>=0.3; extra == 'bayesian'
19
+ Requires-Dist: spyn>=0.0.6; extra == 'bayesian'
20
+ Provides-Extra: dev
21
+ Requires-Dist: fastapi>=0.115; extra == 'dev'
22
+ Requires-Dist: httpx>=0.27; extra == 'dev'
23
+ Requires-Dist: ipykernel; extra == 'dev'
24
+ Requires-Dist: jupyter; extra == 'dev'
25
+ Requires-Dist: pytest-cov; extra == 'dev'
26
+ Requires-Dist: pytest>=7; extra == 'dev'
27
+ Requires-Dist: uvicorn>=0.30; extra == 'dev'
28
+ Provides-Extra: web
29
+ Requires-Dist: fastapi>=0.115; extra == 'web'
30
+ Requires-Dist: httpx>=0.27; extra == 'web'
31
+ Requires-Dist: uvicorn>=0.30; extra == 'web'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # typola
35
+
36
+ Probabilistic models over linguistic typology source data (WALS, Grambank, …).
37
+
38
+ **The core idea** is separation of concerns:
39
+
40
+ - **Data prep** — acquire and canonicalize. Raw CLDF datasets → pandas DataFrames, via one generic loader that works for WALS, Grambank, APiCS, and any other CLDF StructureDataset.
41
+ - **Count-to-probability estimators** — first-class, pluggable, comparable. MLE, Laplace, Jeffreys, Dirichlet-Multinomial, empirical-Bayes, mixtures — pick one, configure it, or plug in your own. Same API.
42
+ - **Probabilistic models** — `Marginal P(parameter|condition)` and `Conditional P(target|given)` built from counts + an estimator.
43
+ - **Query / drill-down** — one entry point (`query`) plus a few small utilities (`compare_estimators`, `cross_validate_estimators`, `rank_associations`, `compare_conditions`) for actually interrogating the model.
44
+
45
+ You can use any layer on its own. The prep layer is just pandas — no probabilistic-model imports required.
46
+
47
+ ## Install
48
+
49
+ ```bash
50
+ pip install -e .
51
+ # with optional bayesian extras (ba + spyn):
52
+ pip install -e '.[bayesian]'
53
+ # with web UI backend:
54
+ pip install -e '.[web]'
55
+ ```
56
+
57
+ ## Web UI
58
+
59
+ A React probability-console frontend (zodal + shadcn) ships in `webapp/`:
60
+
61
+ ```bash
62
+ # 1. Start the API (from repo root):
63
+ python -m webapp.api.main
64
+ # 2. Start the UI:
65
+ cd webapp/ui && npm install && npm run dev
66
+ # open http://127.0.0.1:5173
67
+ ```
68
+
69
+ See `webapp/README.md` for details.
70
+
71
+ ## 60-second tour
72
+
73
+ ```python
74
+ from typola import load, query, estimators
75
+ from typola.query import compare_estimators, cross_validate_estimators, rank_associations
76
+
77
+ # 1. Load a typology. Downloaded & cached on first call.
78
+ wals = load("wals")
79
+ # Typology(name='wals', n_languages=3573, n_parameters=192, n_codes=1143, n_values=76475)
80
+
81
+ # 2. P(Order of Subject and Verb) globally — Jeffreys smoothing.
82
+ d = query(wals, target="81A", estimator=estimators.jeffreys())
83
+ d.top_k(4)
84
+ # name count probability
85
+ # 81A-1 SOV 564 0.409206
86
+ # 81A-2 SVO 488 0.354114
87
+ # 81A-7 No dominant 189 0.137369
88
+ # 81A-3 VSO 95 0.069228
89
+
90
+ # 3. Condition on language metadata.
91
+ query(wals, target="81A", condition={"Family": "Niger-Congo"}).top_k(3)
92
+ # name count probability
93
+ # 81A-2 SVO 277 0.911
94
+ # 81A-7 No dominant 20 0.066
95
+ # 81A-1 SOV 4 0.013
96
+
97
+ # 4. Full conditional P(target | given) — a CPT.
98
+ cpt = query(wals, target="83A", given="81A", estimator=estimators.laplace(0.5))
99
+ cpt.as_matrix() # DataFrame, rows sum to 1
100
+ cpt.p_given("81A-2") # row distribution when subject–verb order is SVO
101
+ cpt.mutual_information() # bits
102
+
103
+ # 5. Compare estimators on the same question.
104
+ compare_estimators(
105
+ wals, target="81A",
106
+ condition={"Family": "Austronesian"},
107
+ estimators=[estimators.mle(), estimators.jeffreys(),
108
+ estimators.empirical_bayes(wals.counts("81A").values, strength=20)],
109
+ )
110
+
111
+ # 6. Actually test which estimator is best — cross-validated log-likelihood.
112
+ cross_validate_estimators(
113
+ wals, target="81A",
114
+ estimators=[estimators.mle(), estimators.laplace(0.1),
115
+ estimators.laplace(0.5), estimators.laplace(1.0),
116
+ estimators.empirical_bayes(wals.counts("81A").values, strength=20)],
117
+ n_folds=5, random_state=0,
118
+ condition={"Family": "Austronesian"},
119
+ )
120
+ # log_likelihood perplexity
121
+ # laplace(alpha=1.0) -44.2886 3.7548
122
+ # laplace(alpha=0.5) -44.3577 3.7648
123
+ # laplace(alpha=0.1) -44.8365 3.8244
124
+ # empirical_bayes(global_counts=..., strength=20.0) -45.3420 3.8896
125
+ # mle() -52.9648 5.2109
126
+
127
+ # 7. Drill down: which parameters are most informative about Subject–Verb order?
128
+ rank_associations(wals, target="81A", top_k=5, estimator=estimators.laplace(0.5))
129
+ # parameter_id parameter_name mutual_information n_languages
130
+ # 0 83A Order of Object and Verb 1.06 1368
131
+ # 1 84A Order of Object, Oblique, and Verb 0.99 486
132
+ # 2 97A Rel. between OV and AdjN 0.97 1190
133
+ # 3 95A Rel. between OV and AdpN 0.96 1039
134
+ # 4 96A Rel. between OV and RelN 0.91 807
135
+ ```
136
+
137
+ Run `python misc/example_bakeoff.py` for the same flow in full.
138
+
139
+ ## Architecture
140
+
141
+ ```
142
+ typola
143
+ ├── sources/ ← source catalog (WALS, Grambank, ...) + downloader
144
+ ├── prep/ ← CLDF → Typology → dol stores
145
+ ├── estimators/ ← count → probability: MLE, Laplace, Jeffreys, Dirichlet, ...
146
+ ├── models/ ← Distribution, Marginal, Conditional
147
+ └── query/ ← query(), compare_estimators(), cross_validate_estimators(), rank_associations(), compare_conditions()
148
+ ```
149
+
150
+ Each layer only depends on the ones above it in the list — so you can use the prep layer without any probabilistic code, or you can use estimators on counts from any other source (not just typola).
151
+
152
+ ## Data sources
153
+
154
+ Currently registered:
155
+
156
+ | Name | License | Source |
157
+ |-------------|--------------|--------|
158
+ | `wals` | CC BY-NC 4.0 | <https://wals.info/> — Dryer & Haspelmath 2013 |
159
+ | `grambank` | CC BY 4.0 | <https://grambank.clld.org/> — Skirgård et al. 2023 |
160
+
161
+ Register more with:
162
+
163
+ ```python
164
+ from typola.sources import register_source, SourceSpec
165
+ register_source(SourceSpec(
166
+ name="apics",
167
+ url="https://github.com/cldf-datasets/apics/archive/refs/heads/master.zip",
168
+ citation="...",
169
+ license="CC-BY-4.0",
170
+ archive_type="zip",
171
+ strip_components=1,
172
+ ))
173
+ ```
174
+
175
+ The loader handles any CLDF StructureDataset that provides `languages.csv`, `parameters.csv`, `codes.csv`, `values.csv`.
176
+
177
+ ## Custom estimators
178
+
179
+ Subclass `Estimator` or just provide any callable with a `.name` attribute:
180
+
181
+ ```python
182
+ from dataclasses import dataclass, field
183
+ from typola.estimators import Estimator
184
+ import numpy as np
185
+
186
+ @dataclass(frozen=True, repr=False)
187
+ class _HaldaneMix(Estimator):
188
+ name: str = "haldane_mix"
189
+ params: dict = field(default_factory=lambda: {"alpha": 0.01})
190
+
191
+ def _estimate(self, counts):
192
+ a = self.params["alpha"]
193
+ smoothed = counts + a
194
+ return smoothed / smoothed.sum()
195
+
196
+ estimators_under_test = [_HaldaneMix(), estimators.jeffreys(), ...]
197
+ ```
198
+
199
+ ## Citing data sources
200
+
201
+ Every `Typology` carries a `.citation` string. Cite it in any downstream output.
202
+
203
+ - **WALS** requires attribution (CC BY-NC 4.0), no commercial use.
204
+ - **Grambank** is CC BY 4.0.
205
+
206
+ ## License
207
+
208
+ MIT.
typola-0.1.1/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # typola
2
+
3
+ Probabilistic models over linguistic typology source data (WALS, Grambank, …).
4
+
5
+ **The core idea** is separation of concerns:
6
+
7
+ - **Data prep** — acquire and canonicalize. Raw CLDF datasets → pandas DataFrames, via one generic loader that works for WALS, Grambank, APiCS, and any other CLDF StructureDataset.
8
+ - **Count-to-probability estimators** — first-class, pluggable, comparable. MLE, Laplace, Jeffreys, Dirichlet-Multinomial, empirical-Bayes, mixtures — pick one, configure it, or plug in your own. Same API.
9
+ - **Probabilistic models** — `Marginal P(parameter|condition)` and `Conditional P(target|given)` built from counts + an estimator.
10
+ - **Query / drill-down** — one entry point (`query`) plus a few small utilities (`compare_estimators`, `cross_validate_estimators`, `rank_associations`, `compare_conditions`) for actually interrogating the model.
11
+
12
+ You can use any layer on its own. The prep layer is just pandas — no probabilistic-model imports required.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install -e .
18
+ # with optional bayesian extras (ba + spyn):
19
+ pip install -e '.[bayesian]'
20
+ # with web UI backend:
21
+ pip install -e '.[web]'
22
+ ```
23
+
24
+ ## Web UI
25
+
26
+ A React probability-console frontend (zodal + shadcn) ships in `webapp/`:
27
+
28
+ ```bash
29
+ # 1. Start the API (from repo root):
30
+ python -m webapp.api.main
31
+ # 2. Start the UI:
32
+ cd webapp/ui && npm install && npm run dev
33
+ # open http://127.0.0.1:5173
34
+ ```
35
+
36
+ See `webapp/README.md` for details.
37
+
38
+ ## 60-second tour
39
+
40
+ ```python
41
+ from typola import load, query, estimators
42
+ from typola.query import compare_estimators, cross_validate_estimators, rank_associations
43
+
44
+ # 1. Load a typology. Downloaded & cached on first call.
45
+ wals = load("wals")
46
+ # Typology(name='wals', n_languages=3573, n_parameters=192, n_codes=1143, n_values=76475)
47
+
48
+ # 2. P(Order of Subject and Verb) globally — Jeffreys smoothing.
49
+ d = query(wals, target="81A", estimator=estimators.jeffreys())
50
+ d.top_k(4)
51
+ # name count probability
52
+ # 81A-1 SOV 564 0.409206
53
+ # 81A-2 SVO 488 0.354114
54
+ # 81A-7 No dominant 189 0.137369
55
+ # 81A-3 VSO 95 0.069228
56
+
57
+ # 3. Condition on language metadata.
58
+ query(wals, target="81A", condition={"Family": "Niger-Congo"}).top_k(3)
59
+ # name count probability
60
+ # 81A-2 SVO 277 0.911
61
+ # 81A-7 No dominant 20 0.066
62
+ # 81A-1 SOV 4 0.013
63
+
64
+ # 4. Full conditional P(target | given) — a CPT.
65
+ cpt = query(wals, target="83A", given="81A", estimator=estimators.laplace(0.5))
66
+ cpt.as_matrix() # DataFrame, rows sum to 1
67
+ cpt.p_given("81A-2") # row distribution when subject–verb order is SVO
68
+ cpt.mutual_information() # bits
69
+
70
+ # 5. Compare estimators on the same question.
71
+ compare_estimators(
72
+ wals, target="81A",
73
+ condition={"Family": "Austronesian"},
74
+ estimators=[estimators.mle(), estimators.jeffreys(),
75
+ estimators.empirical_bayes(wals.counts("81A").values, strength=20)],
76
+ )
77
+
78
+ # 6. Actually test which estimator is best — cross-validated log-likelihood.
79
+ cross_validate_estimators(
80
+ wals, target="81A",
81
+ estimators=[estimators.mle(), estimators.laplace(0.1),
82
+ estimators.laplace(0.5), estimators.laplace(1.0),
83
+ estimators.empirical_bayes(wals.counts("81A").values, strength=20)],
84
+ n_folds=5, random_state=0,
85
+ condition={"Family": "Austronesian"},
86
+ )
87
+ # log_likelihood perplexity
88
+ # laplace(alpha=1.0) -44.2886 3.7548
89
+ # laplace(alpha=0.5) -44.3577 3.7648
90
+ # laplace(alpha=0.1) -44.8365 3.8244
91
+ # empirical_bayes(global_counts=..., strength=20.0) -45.3420 3.8896
92
+ # mle() -52.9648 5.2109
93
+
94
+ # 7. Drill down: which parameters are most informative about Subject–Verb order?
95
+ rank_associations(wals, target="81A", top_k=5, estimator=estimators.laplace(0.5))
96
+ # parameter_id parameter_name mutual_information n_languages
97
+ # 0 83A Order of Object and Verb 1.06 1368
98
+ # 1 84A Order of Object, Oblique, and Verb 0.99 486
99
+ # 2 97A Rel. between OV and AdjN 0.97 1190
100
+ # 3 95A Rel. between OV and AdpN 0.96 1039
101
+ # 4 96A Rel. between OV and RelN 0.91 807
102
+ ```
103
+
104
+ Run `python misc/example_bakeoff.py` for the same flow in full.
105
+
106
+ ## Architecture
107
+
108
+ ```
109
+ typola
110
+ ├── sources/ ← source catalog (WALS, Grambank, ...) + downloader
111
+ ├── prep/ ← CLDF → Typology → dol stores
112
+ ├── estimators/ ← count → probability: MLE, Laplace, Jeffreys, Dirichlet, ...
113
+ ├── models/ ← Distribution, Marginal, Conditional
114
+ └── query/ ← query(), compare_estimators(), cross_validate_estimators(), rank_associations(), compare_conditions()
115
+ ```
116
+
117
+ Each layer only depends on the ones above it in the list — so you can use the prep layer without any probabilistic code, or you can use estimators on counts from any other source (not just typola).
118
+
119
+ ## Data sources
120
+
121
+ Currently registered:
122
+
123
+ | Name | License | Source |
124
+ |-------------|--------------|--------|
125
+ | `wals` | CC BY-NC 4.0 | <https://wals.info/> — Dryer & Haspelmath 2013 |
126
+ | `grambank` | CC BY 4.0 | <https://grambank.clld.org/> — Skirgård et al. 2023 |
127
+
128
+ Register more with:
129
+
130
+ ```python
131
+ from typola.sources import register_source, SourceSpec
132
+ register_source(SourceSpec(
133
+ name="apics",
134
+ url="https://github.com/cldf-datasets/apics/archive/refs/heads/master.zip",
135
+ citation="...",
136
+ license="CC-BY-4.0",
137
+ archive_type="zip",
138
+ strip_components=1,
139
+ ))
140
+ ```
141
+
142
+ The loader handles any CLDF StructureDataset that provides `languages.csv`, `parameters.csv`, `codes.csv`, `values.csv`.
143
+
144
+ ## Custom estimators
145
+
146
+ Subclass `Estimator` or just provide any callable with a `.name` attribute:
147
+
148
+ ```python
149
+ from dataclasses import dataclass, field
150
+ from typola.estimators import Estimator
151
+ import numpy as np
152
+
153
+ @dataclass(frozen=True, repr=False)
154
+ class _HaldaneMix(Estimator):
155
+ name: str = "haldane_mix"
156
+ params: dict = field(default_factory=lambda: {"alpha": 0.01})
157
+
158
+ def _estimate(self, counts):
159
+ a = self.params["alpha"]
160
+ smoothed = counts + a
161
+ return smoothed / smoothed.sum()
162
+
163
+ estimators_under_test = [_HaldaneMix(), estimators.jeffreys(), ...]
164
+ ```
165
+
166
+ ## Citing data sources
167
+
168
+ Every `Typology` carries a `.citation` string. Cite it in any downstream output.
169
+
170
+ - **WALS** requires attribution (CC BY-NC 4.0), no commercial use.
171
+ - **Grambank** is CC BY 4.0.
172
+
173
+ ## License
174
+
175
+ MIT.