Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
c78797f
change: replace `pandas` with `polars`, laid ground works for hea int…
huangziwei Jun 9, 2026
2f3aefa
change: use `hea.io` to load built-in data for tidy verbs; update: no…
huangziwei Jun 9, 2026
1a79733
change: consolidated LC/CCRegression onto `hea.lm` via `harmonic()` b…
huangziwei Jun 9, 2026
e9c0391
add: gam for LC/CCRegression
huangziwei Jun 9, 2026
5314efa
fix: ccregression in t4 notebook
huangziwei Jun 10, 2026
53b80e7
fix: ccreg/plot with se
huangziwei Jun 10, 2026
48d0260
change: distributions/prepare regression compatability for vonmises
huangziwei Jun 10, 2026
afa7368
change: regression/wire the regression ready vonmises into CLRegression
huangziwei Jun 10, 2026
0a6c920
change: pin hea to the dev version for synchronized work
huangziwei Jun 10, 2026
ce03717
add: distributions/links function for regression
huangziwei Jun 10, 2026
b99e212
add: distributions/projectednormal; change: distributions/more regres…
huangziwei Jun 10, 2026
4b6ec7f
add: regression/wire gam into CLRegression
huangziwei Jun 10, 2026
25ead49
change: regression/CLRegression.plot() style
huangziwei Jun 10, 2026
c8e86da
change: regression/CLRegression unified formula style between fisher-…
huangziwei Jun 10, 2026
e69ece6
change: regression/CLRegression.predict() input data format
huangziwei Jun 10, 2026
cc234ab
update: example/T4
huangziwei Jun 10, 2026
8e542cd
update: deps/hea==0.1.4
huangziwei Jun 10, 2026
8123a0b
fix: distributions/various numerica bugs
huangziwei Jun 10, 2026
3178d6c
update: example/t3
huangziwei Jun 10, 2026
0ca7ca4
change: lift some more distributions to be regression ready
huangziwei Jun 11, 2026
a2bcb4f
wip: distributions/bug fix in progress
huangziwei Jun 11, 2026
6f420ca
fix: distributions/jp performance
huangziwei Jun 11, 2026
17aab79
fix: distributions/various .rvs() hang or crashed
huangziwei Jun 11, 2026
5803c9a
fix: distributions/JP-clan cdf/ppf depth gate
huangziwei Jun 11, 2026
11d28f5
fix: distributions/stabler _logpdf everywhere
huangziwei Jun 11, 2026
bfa249b
fix: distributions/wrapstable degenerate case warning in test
huangziwei Jun 11, 2026
a6d90c9
change: distributions/closed-form trig_moment for 9 distributions
huangziwei Jun 11, 2026
c70cace
change: distributions/closed-form trig_moment for JP too
huangziwei Jun 11, 2026
1d0fbe1
change: distributions/close-form cdf and ppf for various distrs
huangziwei Jun 11, 2026
0d10c4a
fix: distributions/align .fit() for all distributions
huangziwei Jun 11, 2026
8647b34
fix: distributions/mostly cosmetics
huangziwei Jun 11, 2026
910ec58
fix: distributions/some wrong bounds and some cosmetics
huangziwei Jun 11, 2026
5435602
add: regression/cl(kj).shape_inference(); fix: docstring here and there
huangziwei Jun 11, 2026
c9a94f3
add: distributions/general family (*lss)
huangziwei Jun 12, 2026
c72a11a
change: gam/catch-up with hea api changes
huangziwei Jun 12, 2026
7a912f0
change: deps/hea==0.1.5.dev
huangziwei Jun 12, 2026
ffb3f2b
add: regression/circ_gam + circ_lm
huangziwei Jun 12, 2026
aa802e2
add: regression/guardrail a hea bug for regression
huangziwei Jun 12, 2026
4270af6
fix: ci
huangziwei Jun 12, 2026
3e9f8fb
fix: ci again
huangziwei Jun 12, 2026
55d07b8
fix: ci/again and again
huangziwei Jun 12, 2026
9fc3c83
fix: regression/role-aware location warm-start in CircularLL.initiali…
huangziwei Jun 15, 2026
67fc118
change: distributions/cardioid _ cartwright now shipped d3/d4
huangziwei Jun 15, 2026
6e7006f
change: regression/circ_gam docstring
huangziwei Jun 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,21 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
- name: Install dependencies
# uv (not pip) so the [tool.uv.sources] git override for hea is
# honored, at the exact commit pinned in uv.lock.
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
pip install -e .
uv sync --locked --python ${{ matrix.python-version }}
uv pip install flake8 pytest
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# (--extend-exclude=.venv: uv sync puts the venv in the workspace, keep it out of lint)
uv run --no-sync flake8 . --extend-exclude=.venv --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
uv run --no-sync flake8 . --extend-exclude=.venv --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest
uv run --no-sync pytest
37 changes: 23 additions & 14 deletions examples/B1-Fisher-1993.ipynb

Large diffs are not rendered by default.

104 changes: 52 additions & 52 deletions examples/B2-Zar-2010.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
}
],
"source": [
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].values[:]\n",
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"c1 = Circular(data=d1)\n",
"\n",
"# Figure 26.2\n",
Expand Down Expand Up @@ -176,7 +176,7 @@
],
"source": [
"d2 = load_data(\"D2\", source=\"zar\")\n",
"c2 = Circular(data=d2[\"θ\"].values[:], w=d2[\"w\"].values[:])\n",
"c2 = Circular(data=d2[\"θ\"].to_numpy()[:], w=d2[\"w\"].to_numpy()[:])\n",
"\n",
"# figure 26.4\n",
"c2.plot(\n",
Expand Down Expand Up @@ -238,7 +238,7 @@
"\n",
"from pycircstat2.utils import data2rad\n",
"\n",
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].values[:]\n",
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"n = len(d1)\n",
"alpha = data2rad(d1, k=360)\n",
"\n",
Expand Down Expand Up @@ -335,8 +335,8 @@
],
"source": [
"d2 = load_data(\"D2\", source=\"zar\")\n",
"alpha = np.asarray(data2rad(d2[\"θ\"].values[:], k=360))\n",
"w = d2[\"w\"].values[:]\n",
"alpha = np.asarray(data2rad(d2[\"θ\"].to_numpy()[:], k=360))\n",
"w = d2[\"w\"].to_numpy()[:]\n",
"select = w != 0\n",
"alpha = alpha[select]\n",
"w = w[select]\n",
Expand All @@ -350,7 +350,7 @@
"\n",
"frame = pl.DataFrame(\n",
" {\n",
" \"α\": d2[\"θ\"].values[select],\n",
" \"α\": d2[\"θ\"].to_numpy()[select],\n",
" \"f\": w,\n",
" \"sin(α)\": sina,\n",
" \"f * sin(α)\": fsina,\n",
Expand Down Expand Up @@ -404,7 +404,7 @@
"source": [
"from scipy.stats import chi2\n",
"\n",
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].values[:]\n",
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"n = len(d1)\n",
"alpha = np.asarray(data2rad(d1, k=360))\n",
"\n",
Expand Down Expand Up @@ -491,7 +491,7 @@
}
],
"source": [
"d3 = load_data(\"D3\", source=\"zar\")[\"θ\"].values[:]\n",
"d3 = load_data(\"D3\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"alpha = np.asarray(data2rad(d3, k=360))\n",
"alpha2 = 2 * alpha % (2 * np.pi)\n",
"sin2a = np.sin(alpha2)\n",
Expand Down Expand Up @@ -569,13 +569,13 @@
"│ --- ┆ --- ┆ --- ┆ --- ┆ --- │\n",
"│ i64 ┆ i64 ┆ f64 ┆ f64 ┆ f64 │\n",
"╞════════╪═════╪════════╪═══════════╪═══════════╡\n",
"│ 1 ┆ 160 ┆ 0.8954 ┆ -0.841401 ┆ 0.306245 │\n",
"│ 2 ┆ 169 ┆ 0.7747 ┆ -0.760467 ┆ 0.14782 │\n",
"│ 3 ┆ 117 ┆ 0.4696 ┆ -0.213194 ┆ 0.418417 │\n",
"│ 4 ┆ 140 ┆ 0.8794 ┆ -0.673659 ┆ 0.565267 │\n",
"│ 5 ┆ 186 ┆ 0.3922 ┆ -0.390051 ┆ -0.040996 │\n",
"│ 6 ┆ 134 ┆ 0.6952 ┆ -0.482926 ┆ 0.500085 │\n",
"│ 7 ┆ 171 ┆ 0.3338 ┆ -0.32969 ┆ 0.052218 │\n",
"│ 0 ┆ 160 ┆ 0.8954 ┆ -0.841401 ┆ 0.306245 │\n",
"│ 1 ┆ 169 ┆ 0.7747 ┆ -0.760467 ┆ 0.14782 │\n",
"│ 2 ┆ 117 ┆ 0.4696 ┆ -0.213194 ┆ 0.418417 │\n",
"│ 3 ┆ 140 ┆ 0.8794 ┆ -0.673659 ┆ 0.565267 │\n",
"│ 4 ┆ 186 ┆ 0.3922 ┆ -0.390051 ┆ -0.040996 │\n",
"│ 5 ┆ 134 ┆ 0.6952 ┆ -0.482926 ┆ 0.500085 │\n",
"│ 6 ┆ 171 ┆ 0.3338 ┆ -0.32969 ┆ 0.052218 │\n",
"└────────┴─────┴────────┴───────────┴───────────┘\n",
"k = 7; n = 10\n",
"∑X = -3.69139; ∑Y = 1.94906\n",
Expand All @@ -598,16 +598,16 @@
],
"source": [
"d4 = load_data(\"D4\", source=\"zar\")\n",
"ms = np.asarray(data2rad(d4[\"mean\"].values[:], k=360))\n",
"rs = d4[\"r\"].values[:]\n",
"ms = np.asarray(data2rad(d4[\"mean\"].to_numpy()[:], k=360))\n",
"rs = d4[\"r\"].to_numpy()[:]\n",
"X = rs * np.cos(ms)\n",
"Y = rs * np.sin(ms)\n",
"i = d4.index.values[:]\n",
"i = np.arange(len(d4))\n",
"\n",
"frame = pl.DataFrame(\n",
" {\n",
" \"Sample\": i,\n",
" \"μ\": d4[\"mean\"].values,\n",
" \"μ\": d4[\"mean\"].to_numpy(),\n",
" \"r\": rs,\n",
" \"X\": X,\n",
" \"Y\": Y,\n",
Expand Down Expand Up @@ -676,7 +676,7 @@
}
],
"source": [
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].values[:]\n",
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"c1 = Circular(data=d1)\n",
"\n",
"n = c1.n\n",
Expand Down Expand Up @@ -735,7 +735,7 @@
}
],
"source": [
"d7 = load_data(\"D7\", source=\"zar\")[\"θ\"].values[:]\n",
"d7 = load_data(\"D7\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"c7 = Circular(data=d7)\n",
"\n",
"n = c7.n\n",
Expand Down Expand Up @@ -790,7 +790,7 @@
}
],
"source": [
"d7 = load_data(\"D7\", source=\"zar\")[\"θ\"].values[:]\n",
"d7 = load_data(\"D7\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"c7 = Circular(data=d7)\n",
"\n",
"if c7.mean_lb < np.deg2rad(90) < c7.mean_ub:\n",
Expand Down Expand Up @@ -837,7 +837,7 @@
}
],
"source": [
"d8 = load_data(\"D8\", source=\"zar\")[\"θ\"].values[:]\n",
"d8 = load_data(\"D8\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"c8 = Circular(data=d8)\n",
"alpha = c8.alpha\n",
"n = len(alpha)\n",
Expand Down Expand Up @@ -918,7 +918,7 @@
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x11d56ee90>]"
"[<matplotlib.lines.Line2D at 0x119d45450>]"
]
},
"execution_count": 19,
Expand All @@ -937,7 +937,7 @@
}
],
"source": [
"d8 = load_data(\"D8\", source=\"zar\")[\"θ\"].values[:]\n",
"d8 = load_data(\"D8\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"c8 = Circular(data=d8)\n",
"alpha = c8.alpha\n",
"n = len(alpha)\n",
Expand Down Expand Up @@ -987,7 +987,7 @@
"metadata": {},
"outputs": [],
"source": [
"d9 = load_data(\"D9\", source=\"zar\")[\"θ\"].values[:]\n",
"d9 = load_data(\"D9\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"c9 = Circular(data=d9)\n",
"\n",
"med = c9.median\n",
Expand Down Expand Up @@ -1033,8 +1033,8 @@
],
"source": [
"d10 = load_data(\"D10\", source=\"zar\")\n",
"s1 = d10[d10[\"sample\"] == 1][\"θ\"].values[:]\n",
"s2 = d10[d10[\"sample\"] == 2][\"θ\"].values[:]\n",
"s1 = d10.filter(pl.col(\"sample\") == 1)[\"θ\"].to_numpy()[:]\n",
"s2 = d10.filter(pl.col(\"sample\") == 2)[\"θ\"].to_numpy()[:]\n",
"\n",
"c10_s1 = Circular(s1)\n",
"c10_s2 = Circular(s2)\n",
Expand Down Expand Up @@ -1079,9 +1079,9 @@
],
"source": [
"d11 = load_data(\"D11\", source=\"zar\")\n",
"s1 = d11[d11[\"sample\"] == 1][\"θ\"].values[:]\n",
"s2 = d11[d11[\"sample\"] == 2][\"θ\"].values[:]\n",
"s3 = d11[d11[\"sample\"] == 3][\"θ\"].values[:]\n",
"s1 = d11.filter(pl.col(\"sample\") == 1)[\"θ\"].to_numpy()[:]\n",
"s2 = d11.filter(pl.col(\"sample\") == 2)[\"θ\"].to_numpy()[:]\n",
"s3 = d11.filter(pl.col(\"sample\") == 3)[\"θ\"].to_numpy()[:]\n",
"\n",
"c11_s1 = Circular(s1)\n",
"c11_s2 = Circular(s2)\n",
Expand Down Expand Up @@ -1130,8 +1130,8 @@
"\n",
"\n",
"d12 = load_data(\"D12\", source=\"zar\")\n",
"s1 = d12[d12[\"sample\"] == 1][\"θ\"].values[:]\n",
"s2 = d12[d12[\"sample\"] == 2][\"θ\"].values[:]\n",
"s1 = d12.filter(pl.col(\"sample\") == 1)[\"θ\"].to_numpy()[:]\n",
"s2 = d12.filter(pl.col(\"sample\") == 2)[\"θ\"].to_numpy()[:]\n",
"\n",
"c12_s1 = Circular(s1)\n",
"c12_s2 = Circular(s2)\n",
Expand Down Expand Up @@ -1163,10 +1163,10 @@
"\n",
"\n",
"d13 = load_data(\"D13\", source=\"zar\")\n",
"s1 = d13[d13[\"sample\"] == 1][\"θ\"].values[:]\n",
"w1 = d13[d13[\"sample\"] == 1][\"w\"].values[:]\n",
"s2 = d13[d13[\"sample\"] == 2][\"θ\"].values[:]\n",
"w2 = d13[d13[\"sample\"] == 2][\"w\"].values[:]\n",
"s1 = d13.filter(pl.col(\"sample\") == 1)[\"θ\"].to_numpy()[:]\n",
"w1 = d13.filter(pl.col(\"sample\") == 1)[\"w\"].to_numpy()[:]\n",
"s2 = d13.filter(pl.col(\"sample\") == 2)[\"θ\"].to_numpy()[:]\n",
"w2 = d13.filter(pl.col(\"sample\") == 2)[\"w\"].to_numpy()[:]\n",
"\n",
"c13_s1 = Circular(data=s1, w=w1)\n",
"c13_s2 = Circular(data=s2, w=w2)\n",
Expand Down Expand Up @@ -1208,8 +1208,8 @@
],
"source": [
"d12 = load_data(\"D12\", source=\"zar\")\n",
"c12_s1 = Circular(data=d12[d12[\"sample\"] == 1][\"θ\"].values[:])\n",
"c12_s2 = Circular(data=d12[d12[\"sample\"] == 2][\"θ\"].values[:])\n",
"c12_s1 = Circular(data=d12.filter(pl.col(\"sample\") == 1)[\"θ\"].to_numpy()[:])\n",
"c12_s2 = Circular(data=d12.filter(pl.col(\"sample\") == 2)[\"θ\"].to_numpy()[:])\n",
"\n",
"from pycircstat2.hypothesis import wheeler_watson_test\n",
"\n",
Expand Down Expand Up @@ -1244,8 +1244,8 @@
],
"source": [
"d14 = load_data(\"D14\", source=\"zar\")\n",
"c14_s1 = Circular(data=d14[d14[\"sex\"] == \"male\"][\"θ\"].values[:])\n",
"c14_s2 = Circular(data=d14[d14[\"sex\"] == \"female\"][\"θ\"].values[:])\n",
"c14_s1 = Circular(data=d14.filter(pl.col(\"sex\") == \"male\")[\"θ\"].to_numpy()[:])\n",
"c14_s2 = Circular(data=d14.filter(pl.col(\"sex\") == \"female\")[\"θ\"].to_numpy()[:])\n",
"\n",
"from pycircstat2.hypothesis import wallraff_test\n",
"\n",
Expand Down Expand Up @@ -1281,8 +1281,8 @@
],
"source": [
"d15 = load_data(\"D15\", source=\"zar\")\n",
"s1 = time2float(d15[d15[\"sex\"] == \"male\"][\"time\"].values[:])\n",
"s2 = time2float(d15[d15[\"sex\"] == \"female\"][\"time\"].values[:])\n",
"s1 = time2float(d15.filter(pl.col(\"sex\") == \"male\")[\"time\"].to_numpy()[:])\n",
"s2 = time2float(d15.filter(pl.col(\"sex\") == \"female\")[\"time\"].to_numpy()[:])\n",
"\n",
"c15_s1 = Circular(data=s1)\n",
"c15_s2 = Circular(data=s2)\n",
Expand Down Expand Up @@ -1323,8 +1323,8 @@
"\n",
"d20 = load_data(\"D20\", source=\"zar\")\n",
"\n",
"a = Circular(data=d20[\"Insect\"].values[:])\n",
"b = Circular(data=d20[\"Light\"].values[:])\n",
"a = Circular(data=d20[\"Insect\"].to_numpy()[:])\n",
"b = Circular(data=d20[\"Light\"].to_numpy()[:])\n",
"\n",
"from pycircstat2.correlation import circ_corrcc\n",
"\n",
Expand Down Expand Up @@ -1358,8 +1358,8 @@
"source": [
"d21 = load_data(\"D21\", source=\"zar\")\n",
"\n",
"a = Circular(data=d21[\"θ\"].values[:]).alpha\n",
"x = d21[\"X\"].values[:]\n",
"a = Circular(data=d21[\"θ\"].to_numpy()[:]).alpha\n",
"x = d21[\"X\"].to_numpy()[:]\n",
"\n",
"from pycircstat2.correlation import circ_corrcl\n",
"\n",
Expand Down Expand Up @@ -1396,8 +1396,8 @@
],
"source": [
"d22 = load_data(\"D22\", source=\"zar\")\n",
"a = Circular(data=d22[\"evening\"].values[:])\n",
"b = Circular(data=d22[\"morning\"].values[:])\n",
"a = Circular(data=d22[\"evening\"].to_numpy()[:])\n",
"b = Circular(data=d22[\"morning\"].to_numpy()[:])\n",
"res = circ_corrcc(a, b, test=True, method=\"nonparametric\")\n",
"\n",
"print(f\"r = {res.r:.5f}, reject? = {res.reject_null}\")"
Expand Down Expand Up @@ -1430,7 +1430,7 @@
],
"source": [
"d2 = load_data(\"D2\", source=\"zar\")\n",
"c2 = Circular(data=d2[\"θ\"].values[:], w=d2[\"w\"].values[:])\n",
"c2 = Circular(data=d2[\"θ\"].to_numpy()[:], w=d2[\"w\"].to_numpy()[:])\n",
"\n",
"from pycircstat2.hypothesis import chisquare_test\n",
"\n",
Expand Down Expand Up @@ -1462,7 +1462,7 @@
}
],
"source": [
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].values[:]\n",
"d1 = load_data(\"D1\", source=\"zar\")[\"θ\"].to_numpy()[:]\n",
"c1 = Circular(data=d1)\n",
"\n",
"from pycircstat2.hypothesis import watson_test\n",
Expand All @@ -1480,7 +1480,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Last updated: 2026-06-08 17:36:42 CEST\n",
"Last updated: 2026-06-09 13:17:14 CEST\n",
"\n",
"Python implementation: CPython\n",
"Python version : 3.13.11\n",
Expand Down
Loading
Loading