Skip to content
Merged

Dev #81

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
521391c
Add option to use real data files in CLI tool
gaggioaxel Oct 9, 2025
10bad5e
Merge branch 'main' into test-gabriele
gaggioaxel Oct 9, 2025
4cc67ca
started testing
gaggioaxel Feb 1, 2026
84d5e7a
Add DirectionChange and Suddenness classes for impulsivity evaluation
gaggioaxel Feb 2, 2026
a7e956b
Refactor DirectionChange and Suddenness classes to return dictionarie…
gaggioaxel Feb 6, 2026
9c7c431
developed and tested geometric symmetry
gaggioaxel Feb 12, 2026
42f0e6e
contraction expansion
gaggioaxel Feb 13, 2026
7453e1d
Refactor Synchronization and SlidingWindow classes for improved funct…
gaggioaxel Feb 14, 2026
938dfd3
Enhance type validation in SlidingWindow and clarify comments in Poin…
gaggioaxel Feb 15, 2026
cb4b9ff
Merge branch 'main' into dev-impulsivity
gaggioaxel Feb 15, 2026
1d92adf
SlidingWindow now has generic signals instead of joints
gaggioaxel Feb 15, 2026
dc82e1c
bug fix
gaggioaxel Feb 19, 2026
7b1fb82
bug fix pt.2
gaggioaxel Feb 19, 2026
21a8066
bug fixed and changed deps due to removal of trapz from numpy>=2.4.0
gaggioaxel Feb 20, 2026
e92d7d4
removed a left print
gaggioaxel Feb 20, 2026
d785409
bug fix
gaggioaxel Feb 25, 2026
0f05078
refactoring
nicola-corbellini Mar 2, 2026
8529131
refactoring
nicola-corbellini Mar 2, 2026
e3fae2e
Rarity test
nicola-corbellini Mar 3, 2026
aba5108
Merge remote-tracking branch 'origin/refactor-sliding-window' into re…
nicola-corbellini Mar 3, 2026
6c3c80f
rarity test
nicola-corbellini Mar 3, 2026
1ae3a55
add example notebooks
nicola-corbellini Mar 3, 2026
69c5e89
rarity test
nicola-corbellini Mar 4, 2026
997b1c6
add feature benchmarks
nicola-corbellini Mar 4, 2026
23d9fdc
update benchmarks
nicola-corbellini Mar 5, 2026
c90a2a7
synchronization benchmark
nicola-corbellini Mar 5, 2026
763744c
remove index
nicola-corbellini Mar 9, 2026
e44e904
add todo
nicola-corbellini Mar 9, 2026
71d7ff8
Add benchmarks for equilibrium and smootness
simoneghisio Mar 11, 2026
b6f0c96
refactor: Remove old test and benchmark data, introduce new benchmark…
nicola-corbellini Mar 11, 2026
b6d4d00
refactor: rename `BaseLowLevelFeature` to `BaseFeature`, update its u…
nicola-corbellini Mar 11, 2026
286e9bc
Aligned Compute_Sparc to article Balasubramanian, S., Melendez-Calder…
simoneghisio Mar 12, 2026
3c74802
Removed absolute path
simoneghisio Mar 12, 2026
575a9cb
feat: add @property to set Smoothness parameters
nicola-corbellini Mar 12, 2026
8adbdf1
feat: Add new analytical primitives for synchronization, kinetic ener…
nicola-corbellini Mar 12, 2026
7ab1722
fix: Correct parameter name from 'methods' to 'metrics' in Statistica…
gaggioaxel Mar 16, 2026
a0fd2b5
add: python docstrings and updated markdown documentation
nicola-corbellini Mar 18, 2026
56a279d
improved documentation
gaggioaxel Mar 19, 2026
ecabc16
add: missing references and links in conceptual documentation
nicola-corbellini Mar 19, 2026
d4e27fe
Add: mike for documentation versioning
nicola-corbellini Mar 20, 2026
85b8fc6
Merge branch 'main' into dev
nicola-corbellini Mar 23, 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
30 changes: 17 additions & 13 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- dev

jobs:
build-deploy:
Expand All @@ -12,6 +13,9 @@ jobs:
# 1. Checkout repo (code + docs)
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GH_PAGES_TOKEN || github.token }}

# 2. Set up Python
- name: Set up Python
Expand All @@ -23,19 +27,19 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[dev] # installs mkdocs and all extras
pip install .[dev] mike

# 4. Build doc
- name: Build MkDocs site
# 4. Deploy MkDocs site with mike
- name: Deploy with mike 🚀
env:
PYTHONPATH: ${{ github.workspace }}/PyEyesWeb
run: mkdocs build --verbose

# 5. Push to gh-pages
- name: Deploy to GitHub Pages 🚀
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GH_PAGES_TOKEN }}
publish_dir: ./site
publish_branch: gh-pages
force_orphan: true
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if [ "${{ github.ref_name }}" = "main" ]; then
mike deploy --push --update-aliases main latest
mike set-default --push latest
else
mike deploy --push ${{ github.ref_name }}
fi
13 changes: 8 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,9 @@ Thumbs.db
*.swp

# Test data or large media files
*.mp4
*.avi
*.mov
*.csv
*.npy
*.h5
*.pytest.py

# Python virtualenv/poetry
poetry.lock
Expand All @@ -44,4 +41,10 @@ dist/
*.egg

demos/Backup
demos/Lib
demos/Lib

# Ignore specific files or directories
__*

# Ignore the site directory
site/
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@ from pyeyesweb.data_models import SlidingWindow
from pyeyesweb.low_level import Smoothness

# Movement smoothness analysis
# 1. Initialize the feature extractor (e.g., 50Hz sampling rate)
smoothness = Smoothness(rate_hz=50.0)
window = SlidingWindow(max_length=100, n_columns=1)
window.append([motion_data])
# here `motion_data` is a float representing a single sample of motion data
# (e.g., the x coordinate of the left hand at time t).

sparc, jerk = smoothness(window)
# 2. Initialize a sliding window for speed data (1 signal, 1 dimension)
window = SlidingWindow(max_length=60, n_signals=1, n_dims=1)

# 3. Process data frame by frame (simulating a real-time loop)
# here `speed_value` is a float representing the instantaneous speed
window.append(speed_value)

# 4. Compute the feature (only after the window is full)
if len(window) >= window.max_length:
result = smoothness(window)
print(f"SPARC: {result.sparc}, Jerk: {result.jerk_rms}")
```
> [!TIP]
> For more advanced and complete use cases see the [Documentation](https://infomuscp.github.io/PyEyesWeb/)
Expand Down
52 changes: 52 additions & 0 deletions docs/javascripts/version-select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
document.addEventListener("DOMContentLoaded", function() {
// Determine base URL based on environment (localhost vs GitHub Pages)
var isLocalhost = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
var baseUrl = isLocalhost ? "" : "/PyEyesWeb";

// Attempt to fetch the versions.json generated by mike
fetch(baseUrl + "/versions.json").then(response => {
if (!response.ok) return null;
return response.json();
}).then(versions => {
if (!versions || versions.length === 0) return;

// Find the current version from the URL path.
var pathSegments = window.location.pathname.split('/').filter(p => p !== "");
// If localhost: /dev/ -> pathSegments[0] is "dev"
// If GitHub: /PyEyesWeb/dev/ -> pathSegments[1] is "dev"
var currentVersion = isLocalhost ? pathSegments[0] : pathSegments[1];

if (!currentVersion) {
currentVersion = versions[0].version; // fallback if at root
}

var select = document.createElement("select");
select.style.margin = "10px auto";
select.style.display = "block";
select.style.width = "90%";
select.style.padding = "5px";
select.style.color = "#000";
select.style.borderRadius = "3px";
select.style.border = "1px solid #ccc";

versions.forEach(function(version) {
var option = document.createElement("option");
option.value = version.version;
option.text = "Version: " + version.title;
if (version.version === currentVersion || version.aliases.includes(currentVersion)) {
option.selected = true;
}
select.appendChild(option);
});

select.addEventListener("change", function() {
window.location.href = baseUrl + "/" + this.value + "/";
});

// Add the dropdown to the readthedocs sidebar search box or navigation container
var searchBox = document.querySelector(".wy-side-nav-search");
if (searchBox) {
searchBox.appendChild(select);
}
}).catch(e => console.error("Error loading versions.json:", e));
});
6 changes: 4 additions & 2 deletions docs/scripts/gen_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def format_module_name(name):


# Generate individual module pages and build nav
for path in sorted(SRC_DIR.rglob("*.py")):
for path in sorted(SRC_DIR.rglob("[!.]*.py")): # Exclude files starting with . (e.g., .pytest.py) from nav
module_path = path.relative_to(SRC_DIR).with_suffix("")
doc_path = API_DOCS_PATH / path.relative_to(SRC_DIR).with_suffix(".md")
module_name = ".".join(module_path.parts)
Expand All @@ -38,7 +38,9 @@ def format_module_name(name):
doc_path.parent.mkdir(parents=True, exist_ok=True)
with mkdocs_gen_files.open(doc_path, "w") as f:
f.write(f"# {format_module_name(module_path.name)}\n\n")
print(f"::: {PACKAGE_NAME}.{module_name}", file=f)
f.write(f"::: {PACKAGE_NAME}.{module_name}\n")
f.write(f" options:\n")
f.write(f" filters: [\"!^_[^_]\"]\n")

mkdocs_gen_files.set_edit_path(doc_path, path)

Expand Down
18 changes: 9 additions & 9 deletions docs/user_guide/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ from pyeyesweb.low_level import Smoothness
smoothness = Smoothness(rate_hz=50.0)

# 2. Create a sliding window to store the last 100 frames of data
window = SlidingWindow(max_length=100, n_columns=1)
window = SlidingWindow(max_length=100, n_signals=1, n_dims=1)

# 3. Simulate a real-time loop reading from a CSV
with open('velocity_data.csv', 'r') as f:
Expand All @@ -57,23 +57,23 @@ with open('velocity_data.csv', 'r') as f:
velocity_val = float(row[0])

# Append the new frame to the sliding window
window.append([velocity_val]) #(1)!
window.append(velocity_val) #(1)!

# Compute smoothness features on the current window
sparc, jerk = smoothness(window) #(2)!
result = smoothness(window) #(2)!

# Check if a valid result was returned
# (feature may return None if window has not enough samples)
if sparc is not None and jerk is not None:
print(f"SPARC: {sparc:.3f} | Jerk: {jerk:.3f}")
# (feature fields may be None if window has not enough samples)
if result.sparc is not None and result.jerk_rms is not None:
print(f"SPARC: {result.sparc:.3f} | Jerk: {result.jerk_rms:.3f}")
```

1. The `SlidingWindow` expects a list of values for every frame (even if there is only 1 sample).
1. `SlidingWindow` automatically handles the data shape. Since we initialized it with `n_signals=1` and `n_dims=1`, we can append a scalar value directly.
As the loop runs, new data is added to the end, and old data is automatically discarded once the max_length is reached.

2. The `smoothness` callable processes the current state of the window.
It returns the SPARC (Spectral Arc Length) and Jerk RMS.
If the window does not yet contain enough data to compute the metric, it may return None.
It returns a result object containing the SPARC (Spectral Arc Length) and Jerk RMS.
If the window does not yet contain enough data to compute the metric, the result fields will be `None`.

## Subpackages

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,21 @@ They summarize, transform, or model data at various temporal and spatial scales.

| Primitive Type | Description | Implemented |
|-------------------------------|--------------------------------------------------------------------------------------------------------------|------------------|
| **Statistical Moments** | Unary operators summarizing distributions (mean, variance, skewness, kurtosis). | :material-close: |
| [**Statistical Moments**](statistical_moments.md) | Unary operators summarizing distributions (mean, variance, skewness, kurtosis). | :material-check: |
| **Shape Descriptors** | Peaks, slopes, valleys in time-series; geometric descriptors of movement curves. | :material-close: |
| **Entropy** [^1] | Approximate/sample entropy, recurrence analysis; quantify predictability or irregularity. | :material-close: |
| [**Entropy**](mse_dominance.md) [^1] | Approximate/sample entropy, recurrence analysis; quantify predictability or irregularity. | :material-check: |
| **Time-Frequency Transforms** | Fourier or wavelet transforms to detect rhythm, periodicity, or temporal structures. | :material-close: |
| **Symmetry** [^2] | Unary/binary operators measuring geometric or dynamic balance (e.g., left vs. right entropy or energy). | :material-close: |
| **Synchronization** [^3][^4] | Binary/n-ary operators measuring alignment of signals (cross-correlation, phase-locking, group entrainment). | :material-close: |
| **Causality** [^4] | Directional relationships (e.g., Granger causality, transfer entropy) to detect leader–follower dynamics. | :material-close: |
| **Clusterability** [^5] | Measures the tendency of data points to form clusters by means of the Hopkins statistics. | :material-close: |
| [**Synchronization**](synchronization.md) [^2][^3] | Binary/n-ary operators measuring alignment of signals (cross-correlation, phase-locking, group entrainment). | :material-check: |
| **Causality** [^3] | Directional relationships (e.g., Granger causality, transfer entropy) to detect leader–follower dynamics. | :material-close: |
| [**Clusterability**](clusterability.md) [^4] | Measures the tendency of data points to form clusters by means of the Hopkins statistics. | :material-check: |
| **Predictive Models** | Hidden Markov Models, classifiers, neural networks; used for gesture segmentation or quality inference. | :material-close: |
| **Saliency / Rarity** [^6] | Detecting unusual occurrences in movement with respect to most frequent patterns. | :material-close: |
| [**Rarity**](rarity.md) [^5] | Detecting unusual occurrences in movement with respect to most frequent patterns. | :material-check: |


## References

[^1]: Glowinski, D., Mancini, M., & Camurri, A. (2013, March). Studying the effect of creative joint action on musicians’ behavior. In International Conference on Arts and Technology (pp. 113-119). Berlin, Heidelberg: Springer Berlin Heidelberg.
[^2]: Glowinski, D., Dael, N., Camurri, A., Volpe, G., Mortillaro, M., & Scherer, K. (2011). Toward a minimal representation of affective gestures. IEEE Transactions on Affective Computing, 2(2), 106-118.
[^3]: Varni, G., Volpe, G., & Camurri, A. (2010). A system for real-time multimodal analysis of nonverbal affective social interaction in user-centric media. IEEE Transactions on Multimedia, 12(6), 576-590.
[^4]: Sabharwal, S. R., Varlet, M., Breaden, M., Volpe, G., Camurri, A., & Keller, P. E. (2022). huSync-A model and system for the measure of synchronization in small groups: A case study on musical joint action. IEEE Access, 10, 92357-92372.
[^5]: Corbellini, N., Ceccaldi, E., Varni, G., & Volpe, G. (2022, August). An exploratory study on group potency classification from non-verbal social behaviours. In International Conference on Pattern Recognition (pp. 240-255). Cham: Springer Nature Switzerland.
[^6]: Niewiadomski, R., Mancini, M., Cera, A., Piana, S., Canepa, C., & Camurri, A. (2019). Does embodied training improve the recognition of mid-level expressive movement qualities sonification?. Journal on Multimodal User Interfaces, 13, 191-203.
[^2]: Varni, G., Volpe, G., & Camurri, A. (2010). A system for real-time multimodal analysis of nonverbal affective social interaction in user-centric media. IEEE Transactions on Multimedia, 12(6), 576-590.
[^3]: Sabharwal, S. R., Varlet, M., Breaden, M., Volpe, G., Camurri, A., & Keller, P. E. (2022). huSync-A model and system for the measure of synchronization in small groups: A case study on musical joint action. IEEE Access, 10, 92357-92372.
[^4]: Corbellini, N., Ceccaldi, E., Varni, G., & Volpe, G. (2022, August). An exploratory study on group potency classification from non-verbal social behaviours. In International Conference on Pattern Recognition (pp. 240-255). Cham: Springer Nature Switzerland.
[^5]: Niewiadomski, R., Mancini, M., Cera, A., Piana, S., Canepa, C., & Camurri, A. (2019). Does embodied training improve the recognition of mid-level expressive movement qualities sonification?. Journal on Multimodal User Interfaces, 13, 191-203.
Loading
Loading