Metadata-Version: 2.4
Name: acycle
Version: 0.6.8
Summary: AcyclePy: Advanced cyclostratigraphy, time series analysis, and paleoclimate toolkit
Author-email: Meng Wang <grampus90@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/mingsongli/AcyclePy
Project-URL: Repository, https://github.com/mingsongli/AcyclePy.git
Project-URL: Issue Tracker, https://github.com/mingsongli/AcyclePy.git/issues
Project-URL: Documentation, https://github.com/mingsongli/AcyclePy/tree/main/doc
Keywords: cyclostratigraphy,time-series,spectral,wavelet,geology
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.20.0
Requires-Dist: pandas>=1.3.0
Requires-Dist: PySide6>=6.0.0
Requires-Dist: matplotlib>=3.3.0
Requires-Dist: scipy>=1.6.0
Requires-Dist: scikit-image>=0.18.0
Requires-Dist: scikit-learn>=0.24.0
Requires-Dist: Pillow>=8.0.0
Requires-Dist: qt-material>=2.0.0
Requires-Dist: astropy>=5.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: black>=22.0.0; extra == "dev"
Requires-Dist: flake8>=4.0.0; extra == "dev"
Provides-Extra: full
Requires-Dist: sounddevice>=0.4.0; extra == "full"
Requires-Dist: h5py>=3.0.0; extra == "full"
Requires-Dist: netCDF4>=1.5.0; extra == "full"
Dynamic: license-file

# AcyclePy — Advanced Cyclostratigraphy and Time Series Analysis Toolkit

[![PyPI](https://img.shields.io/pypi/v/acycle)](https://pypi.org/project/acycle/)
[![Python](https://img.shields.io/pypi/pyversions/acycle)](https://pypi.org/project/acycle/)
[![License](https://img.shields.io/pypi/l/acycle)](LICENSE)

AcyclePy is the Python library companion to the **Acycle** desktop application for cyclostratigraphy, time series analysis, and paleoclimate research. It provides both a programmatic API for scripting and batch processing, and the full suite of Acycle desktop GUI tools.

**Reference**: Li, M., Hinnov, L., & Kump, L. (2019). Acycle: Time-series analysis software for paleoclimate research and education. *Computers & Geosciences*, 127, 12-22.

---

## Installation

### Requirements

- Python >= 3.8
- numpy >= 1.20.0
- scipy >= 1.6.0
- pandas >= 1.3.0
- matplotlib >= 3.3.0
- PySide6 >= 6.0.0 (GUI tools)
- scikit-image >= 0.18.0
- scikit-learn >= 0.24.0
- Pillow >= 8.0.0
- qt-material >= 2.0.0 (GUI theming)
- astropy >= 5.0 (Lomb-Scargle periodogram)

### From PyPI

`ash
pip install acycle
`

### From Source

`ash
git clone https://github.com/mingsongli/AcyclePy.git
cd AcyclePy
pip install -e .
`

### Optional Dependencies

`ash
# Development tools (testing, linting)
pip install "acycle[dev]"

# Full installation with additional I/O support
pip install "acycle[full]"
# Includes: sounddevice, h5py, netCDF4
`

### Verify Installation

`python
import acycle as ac
print(ac.__version__)          # e.g., 0.6.7

s = ac.Series(x=[1,2,3], y=[10,20,30])
print(s)                       # Series(n=3, x=[1, 3] , y_name='y')
`

---

## Quick Start

`python
import acycle as ac
import numpy as np

# 1. Read time series data
# Auto-detect whitespace, tab, or comma-delimited files
s = ac.read_series("data.txt")                    # whitespace-delimited
s = ac.read_series("data.csv", delimiter=",")     # comma-delimited
s = ac.read_series("data.tsv", delimiter="\t")    # tab-delimited

# 2. Chain operations (fluent API)
s2 = s.clean(sort=True)                           \
      .interpolate(step=0.33)                     \
      .detrend(window=80)                         \
      .standardize()

# 3. Spectral analysis
from acycle import spectral
freq, power = spectral._periodogram(s2.y)          # classical
freq, power = spectral._mtm_spectrum(s2.y, nw=3)   # multitaper
freq, power = spectral._lomb_scargle_spectrum(     # uneven spacing
    s2.x, s2.y, fmin=0.001, fmax=0.5)

# 4. Wavelet analysis
from acycle import wavelet
period, power, coi, sig = wavelet.cwt(s2.y, dt=s2.dt)
coh = wavelet.wavelet_coherence(s1.y, s2.y, dt=s1.dt)

# 5. Filtering
from acycle import filter as ft
result = ft.apply_filter(y, dt=0.2,
    kind="bandpass", flow=0.01, fhigh=0.05, method="gaussian")
filtered = result["filtered"]

# 6. Age modeling
from acycle import age
model = age.build_age_model(depth, y, cycle_period=405)
tuned_x, tuned_y = age.tune(depth, y, model)

# 7. Load built-in datasets
lr04   = ac.load_example("lr04")            # LR04 benthic d18O stack (2115 pts)
ceno   = ac.load_example("cenogrid_d18o")   # CENOGRID d18O
la2004 = ac.load_example("la2004_etp")      # La2004 ETP solution
petm   = ac.load_example("petm_logfe")      # Svalbard PETM logFe
`

---

## Detailed Examples

### Example 1: Full Cyclostratigraphic Workflow

`python
import acycle as ac
import numpy as np

# Load LR04 benthic stack
s = ac.load_example("lr04")
print(f"Loaded: {s.n} points, dt={s.dt:.2f} ka")

# Clean and preprocess
s = s.clean(sort=True, dropna=True)

# Interpolate to uniform 1-kyr grid
s = s.interpolate(step=1.0, method="linear")

# Detrend with 400-kyr window
s_dt, trend = s.detrend(window=400, method="lowess", return_trend=True)

# Spectral analysis (MTM)
from acycle import spectral, PSD
freq, power = spectral._mtm_spectrum(s_dt.y, dt=s_dt.dt, nw=3)
psd = PSD(frequency=freq, power=power, method="MTM")
psd.save("lr04_spectrum")        # -> lr04_spectrum_spectrum.csv
psd.plot()

# Wavelet analysis
from acycle import wavelet
period, pow_wav, coi, sig = wavelet.cwt(s_dt.y, dt=s_dt.dt,
    period_min=2, period_max=500, dj=0.05)

# Filter 100-kyr eccentricity band
from acycle import filter as ft, FilterResult
fres = ft.apply_filter(s.y, dt=s.dt,
    kind="bandpass", flow=1/120, fhigh=1/95, method="gaussian")
fr = FilterResult(filtered=ac.Series(x=s.x, y=fres["filtered"]))
fr.to_dataframe()
`

### Example 2: Unevenly-Spaced Data

`python
import acycle as ac
import numpy as np

# Generate uneven synthetic data
x = np.sort(np.random.uniform(0, 1000, 200))
y = np.sin(2*np.pi*x/100) + np.sin(2*np.pi*x/41) + 0.5*np.random.randn(200)

# Lomb-Scargle for uneven spacing
from acycle import spectral
freq, power = spectral._lomb_scargle_spectrum(x, y,
    fmin=0.001, fmax=0.1, pad=2000)

# Find dominant periods
periods = 1.0 / freq
peak_idx = np.argsort(power)[-5:]
for i in peak_idx[::-1]:
    print(f"Period: {periods[i]:.1f}, Power: {power[i]:.3f}")
`

### Example 3: COCO/eCOCO Sedimentation Rate Analysis

`python
import acycle as ac
import numpy as np

# Load data
s = ac.load_example("la2004_etp")

# Interpolate to uniform grid
s = s.interpolate(step=1.0)

# ETP has eccentricity (~405, ~100 kyr), obliquity (~41 kyr), precession
freq, power = ac.spectral._mtm_spectrum(s.y, dt=s.dt, nw=3)

# Build age model by counting 405-kyr cycles
model = ac.age.build_age_model(s.x, s.y, cycle_period=405)
print(f"Tie points: {len(model['tie_points'])}")

# Tune the series
tuned_x, tuned_y = ac.age.tune(s.x, s.y, model)
tuned = ac.Series(x=tuned_x, y=tuned_y, x_name="Age", x_unit="ka")
`

### Example 4: Merge and Compare Multiple Series

`python
import acycle as ac
import numpy as np

# Load multiple datasets
lr04 = ac.load_example("lr04")
ceno = ac.load_example("cenogrid_d18o")

# Resample to common grid
lr04_r = lr04.select(0, 5320).interpolate(step=1.0)
ceno_r = ceno.select(0, 5320).interpolate(step=1.0)

# Merge into DataFrame
from acycle import merge_series
df = merge_series([
    (lr04_r.x, lr04_r.y, "LR04"),
    (ceno_r.x, ceno_r.y, "CENOGRID"),
])

# Multiply two series
x, y = ac.multiply_series(lr04_r.x, lr04_r.y, ceno_r.y, require_same_x=True)
`

### Example 5: Data Preprocessing Pipeline

`python
import acycle as ac
import numpy as np

x = np.arange(0, 100, 0.2)
y = np.sin(2*np.pi*x/20) + 0.1*x + 0.3*np.random.randn(len(x))
s = ac.Series(x=x, y=y, x_name="Depth", x_unit="m", y_name="GR")

# Remove outliers above threshold
s_clipped = s.clip_by_threshold(threshold=2.5, side="above", mode="delete")

# Remove a known bad section (20-25 m)
s_clean, _ = ac.remove_sections(s_clipped.x, s_clipped.y, [(20, 25)])

# Detrend with polynomial
s_dt_lin, trend_lin = ac.detrend(s_clean[0], s_clean[1],
    window=None, method="polynomial", poly_order=1)

# Standardize
s_final = ac.Series(x=s_clean[0], y=s_dt_lin)
s_final = s_final.standardize()
`

---

## Result Objects

All analysis functions return structured result objects with a consistent interface:

| Class | Description | Key Attributes |
|-------|-------------|----------------|
| PSD | Power spectral density | requency, power, period |
| EvolutiveSpectrum | Evolutionary (time-varying) spectrum | x, requency, power |
| WaveletResult | Wavelet transform | x, period, power, coi, significance |
| FilterResult | Filtered signal | iltered, mplitude, phase |
| AgeModel | Age-depth model | depth, ge, sed_rate, 	ie_points |
| CocoResult | COCO/eCOCO correlation | sed_rate, 
ho, p_value |
| SedNoiseResult | DYNOT sedimentation noise | ge, median, quantiles |

All result classes support:

`python
result.to_dataframe()     # export to pandas DataFrame
result.save("prefix")     # save to CSV
result.plot()             # matplotlib figure
result.settings           # dict of computation parameters
`

---

## CLI Tools (Acycle Desktop GUI)

These commands launch the original Acycle desktop GUI tools bundled with the package:

`ash
acycle-imageprocessor          # Image digitizing and data extraction
acycle-plot                    # PlotPro — publication-quality plotting
acycle-interpolation           # Advanced interpolation with gap filling
acycle-data-extractor          # Extract data segments by range
acycle-section-remover         # Remove sections from time series
acycle-gap-adder               # Insert gaps into data
acycle-data-clipper            # Clip data by threshold
acycle-image-analyzer          # Advanced image analysis
`

---

## API Reference

### Series Operations

| Method | Description |
|--------|-------------|
| Series(x, y) | Construct from arrays |
| Series.from_file(path, ...) | Read from delimited text file |
| .clean(sort, duplicate, dropna) | Sort, deduplicate, drop NaN |
| .interpolate(step, method) | Interpolate to uniform grid |
| .interpolate_pro(step, method) | Advanced interpolation with gap filling |
| .interpolate_to(reference) | Interpolate onto another series' x-grid |
| .detrend(window, method, return_trend) | Remove trend |
| .standardize(ddof) | Z-score standardization |
| .log10(handle_nonpositive) | Base-10 logarithm |
| .derivative(order) | Numerical derivative |
| .prewhiten(method) | Prewhitening |
| .select(start, stop) | Sub-range selection |
| .moving_average(n) | Moving average smoothing |
| .gaussian_smooth(n, sigma) | Gaussian smoothing |
| .moving_median(n) | Moving median smoothing |
| .multiply(other_series) | Element-wise multiply with another series |
| .clip_by_threshold(threshold, side, mode) | Clip or remove data by threshold |
| .to_dataframe() | Export to pandas DataFrame |
| .copy() | Deep copy |
| .dt | Median sampling interval (property) |
| .n | Number of points (property) |
| .history | Chain of operations applied |

### Spectral Analysis (cycle.spectral)

| Function | Description |
|----------|-------------|
| _periodogram(y, dt, pad) | Classical periodogram |
| _mtm_spectrum(y, dt, nw, n_tapers, pad) | Multitaper method |
| _lomb_scargle_spectrum(x, y, fmin, fmax, pad) | Lomb-Scargle periodogram |
| _estimate_ar1_rho(y, method) | AR(1) lag-1 autocorrelation |
| _estimate_ar1_noise(y, dt, noise, confidence) | AR(1) noise background |
| _ftest_mtm(y, dt, nw, n_tapers) | F-test for significant peaks |

### Wavelet Analysis (cycle.wavelet)

| Function | Description |
|----------|-------------|
| cwt(y, dt, period_min, period_max, dj, mother, param, pad) | Continuous wavelet transform |
| wavelet_coherence(y1, y2, dt, swap, cross_spectrum) | Wavelet coherence and phase |

### Filtering (cycle.filter)

| Function | Description |
|----------|-------------|
| pply_filter(y, dt, kind, method, flow, fhigh, fcenter, cutoff, order) | Bandpass/lowpass/highpass |
| dynamic_filter(x, y, window, step, fmin, fmax, lower_bound, upper_bound) | Sliding-window dynamic filter |
| mplitude_modulation(x, y, flow, fhigh, method, interpolate_step) | Amplitude envelope extraction |

### Age Modeling (cycle.age)

| Function | Description |
|----------|-------------|
| uild_age_model(x, y, cycle_period, anchor, start_age, age_direction) | Cycle-counting age model |
| sedrate_to_age_model(depth, sedrate, start_age, sedrate_unit) | Sed rate to age model |
| 	une(depth, y, age_model, direction, interpolation) | Depth-to-time conversion |
| stratigraphic_correlation(reference, target, tie_points) | Stratigraphic correlation |

### Preprocessing (cycle.preprocess)

| Function | Description |
|----------|-------------|
| detrend(x, y, window, method, poly_order) | Remove trend |
| clip_by_threshold(x, y, threshold, side, mode) | Clip by value |
| 
emove_sections(x, y, sections, adjust_time) | Remove data ranges |
| dd_gaps(x, y, gaps) | Insert NaN-filled gaps |
| 
emove_peaks(y, ymin, ymax, mode) | Remove or cap peaks |
| multiply_series(x, y1, y2, require_same_x) | Element-wise multiply two series |
| merge_series(series_list, require_same_x) | Merge multiple series by x grid |
| interpolate_pro(x, y, step, method, fill_large_gaps) | Advanced interpolation |
| pca(data, x_col, value_cols, n_components) | Principal component analysis |
| changepoint(y, method, penalty, min_size) | Changepoint detection |
| 	ransform_xy(x, y, a, b, c, d) | Affine coordinate transform |
| ind_extreme(x, y, start, stop, kind) | Find max or min in range |

### I/O (cycle.io)

| Function | Description |
|----------|-------------|
| 
ead_series(path, columns, delimiter, header, ...) | Read Series from file |
| write_series(series, path, sep, header) | Write Series to file |
| load_example(name) | Load built-in dataset |
| load_lr04(start, stop, step) | Load LR04 benthic stack |
| load_cenogrid(variable) | Load CENOGRID isotope data |
| extract_columns(path, x_col, y_col) | Extract columns from multi-column file |

---

## Notes & Common Pitfalls

### pandas Version Compatibility

This package has been tested with pandas >= 2.0. The delim_whitespace parameter (removed in pandas 2.0) has been replaced with sep=r"\s+" throughout the codebase.

### GUI Tools Dependencies

The Acycle desktop GUI tools require a display server (X11, Wayland, or Windows GUI). On headless Linux servers, the programmatic API works without any display. Install with:

`ash
# Headless/server: skip GUI dependencies
pip install numpy scipy pandas matplotlib astropy

# Desktop: full installation
pip install acycle
`

### Lomb-Scargle Periodogram

The Lomb-Scargle implementation prefers stropy.timeseries.LombScargle (faster, more features). If astropy is not installed, falls back to scipy.signal.lombscargle.

### Lowess/Loess Detrending

The detrend(method="lowess") function prefers statsmodels for robust locally weighted regression. If statsmodels is not installed, falls back to a pure-SciPy implementation. Install for better performance:

`ash
pip install statsmodels
`

### File Encoding

By default, 
ead_series() and load_example() use the system locale encoding (commonly cp936/GBK on Chinese Windows). For UTF-8 files, pass encoding="utf-8" explicitly:

`python
s = ac.read_series("data.txt", encoding="utf-8")
`

### Large Files

For files > 100 MB, consider loading with pandas directly and constructing Series objects:

`python
import pandas as pd
import acycle as ac

df = pd.read_csv("large_file.csv", usecols=[0, 1])
s = ac.Series(x=df.iloc[:,0].values, y=df.iloc[:,1].values)
`

---

## Built-in Datasets

| Name | Description | Points | Reference |
|------|-------------|--------|-----------|
| lr04 | LR04 benthic d18O stack | 2,115 | Lisiecki & Raymo (2005) |
| cenogrid_d18o | CENOGRID benthic d18O | 23,659 | Westerhold et al. (2020) |
| cenogrid_d13c | CENOGRID benthic d13C | 23,666 | Westerhold et al. (2020) |
| la2004_etp | La2004 ETP solution | 2,001 | Laskar et al. (2004) |
| petm_logfe | Svalbard PETM logFe | 392 | Charles et al. (2011) |
| wayao_gr | Wayao Carnian GR | 499 | Li et al. (2018) |
| guandao_gr | Guandao Anisian GR | 1,071 | Li et al. (2018) |
| 
ewark_depth_rank | Newark Basin Depth Rank | — | Olsen & Kent (1999) |
| 
ednoise_0.7_2000 | Synthetic red noise (rho=0.7) | 2,000 | — |
| launa_loa_co2 | Mauna Loa CO2 monthly mean | 722 | NOAA |
| csa_extinction | CSA extinction data | 10 | — |

---

## Citation

If you use AcyclePy in your research, please cite:

> Li, M., Hinnov, L., & Kump, L. (2019). Acycle: Time-series analysis software for paleoclimate research and education. *Computers & Geosciences*, 127, 12–22. https://doi.org/10.1016/j.cageo.2019.02.011

## License

MIT License — see [LICENSE](LICENSE) file for details.

## Links

- [PyPI](https://pypi.org/project/acycle/)
- [Repository](https://github.com/mingsongli/AcyclePy)
- [Documentation](https://github.com/mingsongli/AcyclePy/tree/main/doc)
- [Issue Tracker](https://github.com/mingsongli/AcyclePy/issues)
