GreyTheory
GreyTheory is a Python toolkit for grey forecasting, grey relational analysis (GRA), and PCA. Use it to forecast sequences, reduce feature tables, or compare spectra and measurements.
| Task | Start here |
|---|---|
| Forecast the next few values | GM(1,1) |
| Forecast with two levels of windows | Convolution |
| Compare features with a target | GRA |
| Reduce dimensions, then select components | GreyPCA |
| Score mixed quality preferences | Quality scoring |
| Work with paths or images | Feature extraction |
| Read PNG/JPG files for comparison | Image-file example |
| Read Bruker files | SPX/BCF |
| Run synthetic spectra and AI handwriting | Both demos |
Get started
Requires Python 3.11 or later. Download or clone the repository, then run from its directory:
python3 run.py
To use GreyTheory in your own program, install it from the project directory with that program's Python environment:
python -m pip install .
Time-series forecasting
Pass observations to fit, then call predict(2) for the next two values:
from greytheory import GM11
series = [223.3, 227.3, 230.5, 238.1, 242.9, 251.1]
model = GM11(background_mode="corrected").fit(series)
print(model.predict(2))
print(model.metrics)
Use at least three observations. See Convolution below for two-level window forecasting.
Read coefficients from model.coefficients and solver diagnostics from model.diagnostics.
set_data stores observations for a later batch run. reset() clears results;
reset(clear_data=True) also clears the data.
Background weights, regularization, and errors
background_mode="corrected" uses alpha * current + (1-alpha) * previous.
The default "legacy" mode multiplies both terms by alpha. They agree at alpha=0.5.
The solver uses least squares. Set ridge_alpha for regularization or
rank_policy="raise" to reject fits without uniquely identifiable coefficients.
Diagnostics include rank, condition number, and residuals.
horizon=0 fits history only. Relative error is None for a zero observation;
MAE and RMSE remain available. predict leaves stored fit results unchanged.
Convolution forecasting
Set window_size to enable convolution. Each window uses GM(1,1) to predict its next value;
a second GM(1,1) then fits those predictions:
Sequence → window predictions → second-level GM(1,1) → future values
from greytheory import GM11
series = [223.3, 227.3, 230.5, 238.1, 242.9, 251.1]
model = GM11(window_size=4, stride=1, background_mode="corrected").fit(series)
print(model.predict(2))
| Setting | Meaning |
|---|---|
window_size=4 |
Four observations per window; None uses ordinary GM11 |
stride=1 |
Advance one observation at a time |
predict(2) |
Forecast two future values |
correction_mode="legacy" |
Keep the default window-error correction; "none" disables it |
For the full two-level method, provide enough data for at least three windows.
The six observations above, with width four and stride one, produce three windows.
coefficients and metrics describe the final fitted level. The last window can include extra tail observations, matching the original method.
Run the comparison to see both ordinary and convolution forecasts:
.venv/bin/python examples/convolution_forecast.py
Example output:
Ordinary GM11, next two values: [256.5532, 263.1223]
Convolution GM11, next two values: [265.9278, 274.0119]
GreyWindowFeatures instead extracts local descriptors for PCA/GRA; it does not run the second forecast level.
Multiple-factor models
GM0N and GM1N model several factors against one target.
Each row is an observation and each column is a factor:
from greytheory import GM0N
factors = [[.75, .5], [1.22, 1], [.2, .7], [1, .66], [1, 1], [1, .5]]
targets = [1, 1, 1, 1, 1, 1]
model = GM0N().fit(factors, targets, feature_names=["factor_a", "factor_b"])
print(model.coefficients)
print(model.influence_degrees)
For GM(1,N), use GM1N().fit(factors, targets). Feature names are optional.
influence_degrees ranks absolute coefficients, which depend on units and collinearity.
Use GRA below to compare sequences with a reference.
Grey relational analysis (GRA)
Supply a reference sequence and candidate sequences. Higher scores mean closer matches under the chosen normalization:
from greytheory.analysis import grey_relation
scores = grey_relation(
reference=[0.2, 0.5, 0.8],
candidates=[[0.2, 0.5, 0.8], [0.8, 0.1, 0.2]],
rho=0.5,
normalize="none",
)
print(scores)
normalize accepts none, minmax, zscore, initial, or mean.
You can also import GreyGRA from greytheory.analysis and use .score(...) or .rank(...).
Scores depend on the candidate batch, so adding or removing candidates can change the ranking. They measure grey relation, not causal importance or prediction accuracy.
GRA formula
For each absolute difference Δ, the coefficient is
(Δmin + rho*Δmax) / (Δ + rho*Δmax). The score is a weighted mean.
rho must be in (0, 1]; weights must be nonnegative.
Difference extrema come from the same candidate batch. A score of 1 need not mean identical raw values.
PCA and feature selection
GreyFeatureSelector selects original columns. GreyPCA reduces dimensions, then uses
GRA to select components against a target. This example uses synthetic data:
import numpy as np
from sklearn.model_selection import train_test_split
from greytheory.analysis import GreyFeatureSelector, GreyPCA
random_generator = np.random.default_rng(7)
factors = random_generator.normal(size=(48, 6))
targets = factors[:, 0] + 0.05 * random_generator.normal(size=48)
train_factors, test_factors, train_targets, _ = train_test_split(
factors, targets, random_state=7
)
selector = GreyFeatureSelector(k=2).fit(train_factors, train_targets)
selected_factors = selector.transform(test_factors)
analysis = GreyPCA(
n_components=4, method="sparse", method_params={"alpha": 0.1},
top_k=2, standardize=True,
).fit(train_factors, train_targets)
components = analysis.transform(test_factors)
print(analysis.selected_components)
print(analysis.component_scores)
n_components sets the decomposition size; top_k sets how many components GRA keeps.
Omit the target in fit(train_factors) to run dimensionality reduction alone.
Switch PCA methods
Change method and pass its settings in method_params:
method |
Decomposition | Example method_params |
|---|---|---|
"pca" (default) |
PCA | {"whiten": True} |
"sparse" |
SparsePCA | {"alpha": 0.1} |
"kernel" |
KernelPCA | {"kernel": "rbf", "gamma": 0.1} |
"robust" |
PCA projection learned from a PCP low-rank matrix | {"tol": 1e-6} |
"robust_sparse" |
Huber/elastic-net RobustSparsePCA | {"alpha": 0.1, "kappa": 1.0} |
"adv" |
AdvPCA | {"delta": 0.01, "max_iter": 1000} |
For example, switch to AdvPCA:
analysis = GreyPCA(
3, method="adv", method_params={"delta": 0.01, "max_iter": 1000},
top_k=2, standardize=True,
).fit(train_factors, train_targets)
components = analysis.transform(test_factors)
print(analysis.reducer.converged)
print(analysis.reducer.inner_converged)
Check both outer and final inner convergence before evaluating AdvPCA results. See grey_pca_methods.py for a comparison of the methods.
You can also supply a sklearn-compatible model with its own component count:
from sklearn.decomposition import IncrementalPCA
analysis = GreyPCA(
estimator=IncrementalPCA(n_components=4, batch_size=12), top_k=2,
).fit(train_factors, train_targets)
Custom models need fit, transform, and sklearn clone support, and must return a dense
2D matrix with one row per observation. Use either estimator or method/method_params.
Inspect selected components and rankings
component_scores contains GRA scores. selected_components contains zero-based indices;
output columns retain their original component order. To list scores from highest to lowest:
valid = np.flatnonzero(np.isfinite(analysis.component_scores))
ranking = valid[np.argsort(-analysis.component_scores[valid], kind="stable")]
for index in ranking:
print(f"PC{index + 1}", float(analysis.component_scores[index]))
Original-column scores are in selector.scores. Without a target, component_scores is None.
This selection works with continuous targets. Writer IDs and other categorical labels need
a separate classification or comparison model.
Choose the component count
For ordinary PCA, you can specify the cumulative variance to retain, such as 85%:
ordinary = GreyPCA(n_components=0.85, method="pca", standardize=True).fit(train_factors)
print(ordinary.n_components_fitted)
print(ordinary.explained_variance_ratio.sum())
report = analysis.reconstruction_report(train_factors, threshold=0.85)
print(report.prefix_coverage)
print(report.first_prefix_at_threshold)
print(report.selected_coverage)
Fractional n_components is supported only for ordinary PCA. For AdvPCA or SparsePCA,
inspect reconstruction coverage instead. An 80–85% threshold is a starting point;
use held-out data to check prediction performance, since low-variance components can still carry quality signals.
Standalone decompositions and spectral PCA
Import these from greytheory.analysis to use them without GRA:
| Call | Purpose |
|---|---|
PCAAdapter(3, method="pca") |
PCA |
PCAAdapter(3, method="sparse", alpha=0.1) |
SparsePCA |
PCAAdapter(3, method="kernel", gamma=0.1) |
KernelPCA |
RobustPCA().fit(data) |
Exposes low_rank and sparse matrices |
RobustSparsePCA(3).fit(data) |
Huber/elastic-net sparse projection |
AdvPCA(3, delta=0.05).fit(data) |
AdvPCA projection |
Import spectral methods from greytheory.spectroscopy:
| Call | Required input |
|---|---|
PoissonPCA(3).fit_transform(counts) |
Nonnegative counts; approximate variance stabilization |
fit_mlpca(data, variances, n_components=3) |
Independent errors with positive variance for each entry |
fit_mnf(cube, n_components=3) |
Spatial cube; noise estimated from neighboring pixels |
Use GreyPCA(estimator=PoissonPCA(3), standardize=False) to combine PoissonPCA with GRA.
Keep counts nonnegative; do not apply standardization that produces negative values.
Component signs, reconstruction, and method differences
PCA component signs can flip. By default, component_direction="either" compares both signs
in one GRA batch and keeps the higher score. Constant components get NaN scores and are excluded;
top_k=None keeps all scorable components. GreyFeatureSelector compares the original direction
by default; set direction="either" to compare both.
With a quality target, add align_to_target=True to orient output signs toward the reference.
Signs are fixed during training; tied scores raise an error.
Inspect component_directions and component_direction_ambiguous for the decisions.
This handles sign choices, not physical monotonicity or component swaps/rotations across refits.
Set component_direction="positive" for signed GRA comparison.
inverse_transform fills omitted components with zeros. KernelPCA reconstruction is approximate;
some custom models have no inverse. reconstruction_report measures 1 - SSE / SST for the
specified components without changing their count. With standardize=True, it uses standardized space.
For other decompositions, coverage can be negative or nonmonotone.
first_prefix_at_threshold=None means no evaluated prefix reached the threshold;
check selected_coverage separately after GRA selection.
Prefixes zero later components of one fitted model; refitting fewer components may give different results.
"robust" learns its projection from PCP's low-rank training matrix. New rows use that projection
without another PCP cleanup. AdvPCA's sparse-encoding objective differs from PCP's low-rank/additive-outlier split.
RobustSparsePCA and AdvPCA involve nonconvex optimization; check convergence diagnostics.
Quality scoring
For example, lower warpage and higher shear force can both be desirable. Convert them to 0–1 benefit values, then compute a weighted GRA grade:
from greytheory.analysis import rank_quality, grade_quality
quality = rank_quality(
[[80, 10], [40, 20], [20, 25]], # Rows: samples. Columns: warpage, shear force.
directions=["smaller", "larger"],
bounds=[(0, 100), (0, 30)], # Example bounds; replace with process-specific values.
weights=[0.5, 0.5],
)
print(quality.normalized_values)
print(quality.scores)
print(quality.ranking) # [2, 1, 0]: row indices, best first
new_benefits = quality.normalizer.transform([[30, 22]])
new_quality = grade_quality(new_benefits, weights=quality.weights, rho=quality.rho)
print(new_quality.scores)
directions accepts larger, smaller, or target.
For thickness near 100, use BenefitNormalizer(["target"], bounds=[(80, 120)], targets=[100])
with fit_transform or transform. Import it from greytheory.analysis.
bounds sets the normalization range. If omitted, it uses training minima and maxima.
Out-of-range values raise by default; out_of_range="clip" clips them to the bounds.
Use normalizer.out_of_bounds(data) to locate the original violations.
Making original columns larger-is-better does not make every PCA component larger-is-better; see the component-sign notes above.
See semiconductor_adv_quality.py for process prediction. It keeps process features separate from quality targets, splits by lot, and compares AdvPCA/GRA + Ridge against a mean baseline. Do not feed warpage/shear values used to define the target back into the predictor's inputs.
Scoring formula and input checks
Calculation
grade_quality computes a coefficient for each response, then takes a weighted mean:
coefficient = rho / (1 - benefit + rho)
score = weighted_mean(coefficients)
| Setting | Value |
|---|---|
| Ideal sequence | All ones |
| Minimum / maximum difference | Fixed at 0 / 1 |
| Aggregation | Weighted mean; responses can compensate for each other |
grey_relation instead takes difference extrema from the candidate batch.
NIST desirability uses a geometric mean, so its calculation also differs.
Input checks
| Check | Requirement |
|---|---|
| Values | No NaN or infinity |
| Columns | Matching count, directions, names, and order |
| Constant columns | Valid normalization bounds must be supplied |
target |
Strictly inside its bounds; uses a piecewise linear benefit peak |
| Weights | Nonnegative, with at least one positive weight |
Quality normalization is separate from AdvPCA's perturbation radius delta.
Trajectory, image, and window features
Build a pipeline from numeric inputs:
Data → trajectory/image features → Grey window features → PCA/GRA → regression or distance comparison
| Input | Shape | Use |
|---|---|---|
| Spectra or scalar sequences | (samples, points) |
GreyWindowFeatures; align spectral energy axes first |
| 2D and higher-dimensional paths | List of (points, dimensions) arrays |
TrajectoryFeatures → GreyWindowFeatures |
| Grayscale/multichannel images | (samples, height, width[, channels]) |
ImagePatchFeatures → GreyWindowFeatures |
Here is a 2D path with timestamps and stroke IDs. The same class handles 3D and 5D paths:
from greytheory.features import Trajectory, TrajectoryFeatures, GreyWindowFeatures
path = Trajectory(
points=[[0, 0], [1, 1], [2, 1], [10, 10], [11, 10], [12, 11]],
timestamps=[0, 0.5, 1, 3, 3.5, 4],
stroke_ids=[0, 0, 0, 1, 1, 1],
)
sequences = TrajectoryFeatures(n_points=16, include_time=True).fit_transform([path])
windows = GreyWindowFeatures(window_size=8, stride=4, boundary_channel=-1)
features = windows.fit_transform(sequences)
print(features.shape)
paths_3d = [[[0, 0, 0], [1, 2, 1], [2, 2, 3]]]
sequences_3d = TrajectoryFeatures(n_points=16).fit_transform(paths_3d)
paths_5d = [[[0, 0, 0, 0, 0], [1, 2, 1, 3, 2], [2, 2, 3, 4, 5]]]
sequences_5d = TrajectoryFeatures(n_points=16).fit_transform(paths_5d)
To read image files directly, see “Start with image files” below.
To add regression, use sklearn's Pipeline. Here, train_paths and test_paths are lists of
Trajectory objects; train_targets contains one continuous value per training path:
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
from greytheory.analysis import GreyPCA
model = Pipeline([
("trajectory", TrajectoryFeatures(n_points=32, include_time=True)),
("windows", GreyWindowFeatures(8, stride=4, boundary_channel=-1)),
("reducer", GreyPCA(4, top_k=2, standardize=True)),
("model", Ridge(alpha=1)),
])
model.fit(train_paths, train_targets)
predictions = model.predict(test_paths)
See grey_feature_pipeline.py for a runnable example. These features can feed a model; handwriting authorship and overpainting detection still need dedicated models and validation data.
Start with image files
Install image support once:
.venv/bin/python -m pip install -e ".[images]"
Supply at least three reference images and a separate query image. Choose any directory; this example uses:
test_data/images/
├── reference/
│ ├── sample_a.png
│ ├── sample_b.jpg
│ └── sample_c.png
└── query.png
load_images accepts one path or a list of paths and returns an RGB batch in [0, 1].
Read PNG/JPG files and rank references by their distance from the query:
from pathlib import Path
import numpy as np
from sklearn.metrics import pairwise_distances
from sklearn.pipeline import Pipeline
from greytheory.io import load_images
from greytheory.features import ImagePatchFeatures, GreyWindowFeatures
from greytheory.analysis import GreyPCA
reference_paths = sorted(
path for path in Path("test_data/images/reference").iterdir()
if path.is_file() and path.suffix.lower() in {".png", ".jpg", ".jpeg"}
)
reference_images = load_images(reference_paths, size=(128, 128))
query_image = load_images("test_data/images/query.png", size=(128, 128))
pipeline = Pipeline([
("patches", ImagePatchFeatures(grid_shape=(4, 8))),
("windows", GreyWindowFeatures(8, stride=8, boundary_channel=-1)),
("pca", GreyPCA(2, standardize=True)),
])
reference_components = pipeline.fit_transform(reference_images)
query_components = pipeline.transform(query_image)
distances = pairwise_distances(query_components, reference_components)[0]
for index in np.argsort(distances, kind="stable"):
print(reference_paths[index].name, float(distances[index]))
Distances are measured in PCA feature space; smaller means closer. Fit on reference images only, then transform the query. These distances do not establish authorship, authenticity, or overpainting.
size=(128, 128) is (width, height) and preserves aspect ratio with white padding.
Without it, all image dimensions must match. Loading applies EXIF orientation and composites
transparency over white; use mode="L" for grayscale.
Align crops and positions beforehand. Keep high-bit-depth scientific intensities as numeric arrays
rather than converting them through this display-image loader.
Or run the script directly. The first command generates demo PNGs; the second uses your files:
.venv/bin/python examples/image_files.py
.venv/bin/python examples/image_files.py \
--references test_data/images/reference \
--query test_data/images/query.png \
--size 128 128
Results are written to artifacts/image_files/ by default:
| File | Contents |
|---|---|
features.csv |
Grey window descriptors for each image |
components.csv |
PCA coordinates |
ranking.csv |
Reference distances and ranking |
Full example: image_files.py.
Path, window, and image input requirements
Paths are resampled by stroke arc length by default, keeping position, direction, distance,
turning angle, and stroke boundaries. include_time=True resamples by time and adds speed;
timestamps must increase strictly. Each stroke needs at least two input points, and
n_points must be at least twice the stroke count. Pen-up gaps are excluded from distance and speed.
center=True removes the start position; scale=False keeps spatial units. Speed uses coordinate/time units
and angles use radians. With scale=True, distances and speeds use normalized units.
Keep time and pressure out of spatial coordinates; dedicated pressure handling is not provided.
Grey windows extract amplitude, mean, standard deviation, within-segment changes, GM coefficients,
equation RMSE, and coverage. They do not forecast. Each window/channel is scaled by its maximum absolute value,
with original amplitude retained. Fragments shorter than three points get zero GM coefficients and gm_coverage;
check gm_full_rank_coverage too. Low coverage makes coefficients less informative.
boundary_channel=-1 prevents fits across strokes or image-grid rows. Keep stride no larger than the window.
Images must be numeric arrays. Inspect positions with patch_bounds and windows, and names with
get_feature_names_out(). Image features do not include OCR or character segmentation;
static images cannot provide actual writing speed or pressure.
Bruker file readers
from greytheory.io.bruker import read_bcf, read_spx
spectrum = read_spx("reference.spx")
image = read_bcf("scan.bcf", lazy=True)
counts = image.get_pixel(y=0, x=0, detector=0)
energy_keV = image.energy_axes[0]
roi = image.get_spectra((0, 0, 2, 2), detector=0)
SPX exposes counts, metadata, and energy calibration when present. BCF keeps each detector's calibration.
ROI bounds are (y0, x0, y1, x1), excluding the upper bounds; counts use uint64.
BCF support covers indexed, instructively packed pixels in uncompressed SFS streams.
Other encodings, outer compression, and BCF v2 are unsupported. sum_detectors rejects mismatched energy axes.
Check readings against the instrument software for your files.
Process and spectroscopy tools
| Module | Tools |
|---|---|
greytheory.semiconductor |
PLS quality regression, dynamic/multiway PCA monitoring, GRA ranking, Latin-hypercube sampling, GP expected-improvement proposals |
greytheory.spectroscopy |
AsLS baseline correction, NMF, MCR-ALS, MNF, Poisson PCA, weighted low-rank fitting, X-ray line lookup, fixed-center peak fitting |
Examples: synthetic spectra, held-out manufacturing lots, AdvPCA quality prediction. Converting XRF peak amplitudes to concentrations requires instrument, standard-sample, and matrix calibration.
Batch execution
Reuse a model with different data. ModelQueue saves a deep copy when each entry is added:
from greytheory import GM11, ModelQueue
if __name__ == "__main__":
queue = ModelQueue()
model = GM11(background_mode="corrected")
for series in ([2, 3, 4, 5, 6], [3, 4, 6, 8, 11]):
queue.append(model.set_data(series))
results = queue.run(workers=2)
print([item.predict(2) for item in results])
queue.run() is sequential by default. Set workers=2 for two processes; results keep input order.
You can mix GM0N, GM1N, and GM11. For an existing model list, use run_models(models, workers=2).
Keep the if __name__ == "__main__": guard when using processes.
Queue copying and updates
Construction, insertion, and replacement deep-copy models. Reading queue[0] returns the stored model,
which can be edited. The queue updates only after all jobs succeed.
Use inplace=False to return fitted copies without changing the queue.
Synthetic XRF and AI handwriting demos
The synthetic inputs and results are included.
Download the project and open examples/results/synthetic_demos/index.html in a browser; Python is not needed to view them.
GitHub does not render the HTML reports directly; use the images and CSV links below for an online preview.
To rerun both demos, run these commands in the project directory (macOS/Linux):
python3 -m venv .venv
.venv/bin/python -m pip install -e ".[demos]"
.venv/bin/python examples/synthetic_demos.py
On Windows, replace .venv/bin/python with .venv\Scripts\python.exe.
| Demo | Input and analysis | Output |
|---|---|---|
| Synthetic XRF | Four 6×6 maps, 144 spectra with 1024 channels; known Ca/Fe/Cu/Zn line-template weights, continuum, and Poisson noise | Spectra, energy-channel GRA ranking, and PCA/GreyPCA prediction on a held-out simulated map |
| AI handwriting styles | 18 cells of 春風明月 in three prompt-defined styles; 12 references and 6 queries | Patch/PCA versus Grey-window/PCA distances, coordinates, and numeric feature exports |
New results go to artifacts/synthetic_demos/; open its index.html.
For one demo, append --demo xrf or --demo handwriting to the script command.
| What to inspect | Included files |
|---|---|
| Spectral counts, energy axis, and synthetic truth | spectra.csv, samples.csv; each MSA in emsa/ contains one spectrum |
| Energy ranking and prediction comparison | energy_ranking.csv, predictions.csv |
| Handwriting queries and complete ranking | queries.csv, ranking.csv |
To refresh the bundled results, run .venv/bin/python examples/synthetic_demos.py --output examples/results/synthetic_demos.
Or call the modules directly:
from greytheory.demos import run_xrf_demo, run_handwriting_demo
xrf = run_xrf_demo("artifacts/synthetic_demos/xrf", seed=7)
handwriting = run_handwriting_demo("artifacts/synthetic_demos/handwriting")
print(xrf["report"])
print(handwriting["report"])
XRF exports emsa/*.msa, spx/*.spx, spectra.csv, and dataset.npz.
Bruker documents EMSA exchange, but importing these files still needs testing with your actual M4/ESPRIT version.
The SPX files are minimal fixtures for this project's reader, not guaranteed vendor exports.
Synthetic mixing weights are not calibrated elemental wt%.
The handwriting sheet was created with imagegen; the image and prompt record are bundled. Labels describe prompt-assigned styles, not human writers. All samples share one generated sheet; style matches demonstrate the workflow, not cross-document or human-authorship accuracy. Grey means window descriptors here; categorical style IDs are never used as continuous GRA targets.
Preview the spectra and AI handwriting results
Development and tests
python -m pip install -e . -r requirements-dev.txt
python -m pytest -q
python -m build
Test inputs are generated or included in the repository; no dataset download is needed.
scripts/verify_full_install.py checks the installed APIs. Examples use synthetic data to demonstrate
the workflow, not establish process, chemical, or forensic accuracy.
To prepare a GitHub source ZIP, run python3 scripts/package_github.py.
The archive in artifacts/github_upload/ includes source and synthetic results, excluding local environments, caches, and external instrument datasets.
Benchmark records are in benchmarks/results.
Rerun the core benchmark or decomposition comparison.
Recorded timings are from macOS arm64 and depend on the environment.
The older GreyTheory, GreyGM*, add_pattern/add_patterns, and grey.run.gm* APIs remain available.
License
MIT. Dependencies retain their own licenses; see Third-party notices.
References
Algorithms, upstream tools, and further reading. SCRAMBLE is related research, not an included implementation.
-
Deng, J. L. (1982). Control problems of grey systems. Systems & Control Letters, 1(5), 288–294.
-
Liu, S. & Lin, Y. (2011). Grey Systems: Theory and Applications. Springer. Grey modelling and relational analysis.
-
Kayacan, E., Ulutas, B. & Kaynak, O. (2010). Grey system theory-based models in time series prediction. Expert Systems with Applications, 37(2), 1784–1789.
-
NumPy. Least-squares solver and rank diagnostics; accurate evaluation of exp(x) − 1.
-
Jolliffe, I. T. & Cadima, J. (2016). Principal component analysis: a review and recent developments. Philosophical Transactions A, 374, 20150202.
-
scikit-learn. PCA, SparsePCA and KernelPCA implementations; training/test preprocessing; custom estimator cloning.
-
Candès, E. J., Li, X., Ma, Y. & Wright, J. (2011). Robust Principal Component Analysis?. Journal of the ACM, 58(3), Article 11. Principal Component Pursuit.
-
Lin, Z., Chen, M. & Ma, Y. (2010). The Augmented Lagrange Multiplier Method for Exact Recovery of Corrupted Low-Rank Matrices. Inexact ALM solver for PCP.
-
Erichson, N. B. et al. (2020). Sparse Principal Component Analysis via Variable Projection, author manuscript. SIAM Journal on Applied Mathematics, 80(2), 977–1002. Robust sparse model: equation (17), Algorithm 2.
-
Vävinggren, D. et al. (2026). A Robust Optimization Approach to Sparse Principal Component Analysis. AdvPCA: Algorithms 1–2 and Appendix A.3.
-
Pfeiffer, P., Vana-Gür, L. & Filzmoser, P. (2025). Cellwise robust and sparse principal component analysis. SCRAMBLE; related work, not the algorithm used by
RobustSparsePCA. -
Lee, D. D. & Seung, H. S. (1999). Learning the parts of objects by non-negative matrix factorization. Nature, 401, 788–791. NMF background; this package delegates fitting to scikit-learn.
-
Camp, C. H. Jr. (2019). pyMCR: A Python Library for Multivariate Curve Resolution Analysis with Alternating Regression (MCR-AR). Journal of Research of NIST, 124, 124018.
-
Green, A. A., Berman, M., Switzer, P. & Craig, M. D. (1988). A transformation for ordering multispectral data in terms of image quality with implications for noise removal. IEEE Transactions on Geoscience and Remote Sensing, 26(1), 65–74. Spectral Python MNF API.
-
Anscombe, F. J. (1948). The transformation of Poisson, binomial and negative-binomial data. Biometrika, 35(3–4), 246–254. Variance stabilization used by
PoissonPCA. -
Wentzell, P. D. et al. (1997). Maximum likelihood principal component analysis. Journal of Chemometrics, 11(4), 339–366.
fit_mlpcaimplements the independent-error, fixed-variance case. -
pybaselines. Asymmetric least-squares baseline correction. The
baseline_correctimplementation usesBaseline.asls. -
Wold, S., Sjöström, M. & Eriksson, L. (2001). PLS-regression: a basic tool of chemometrics. Chemometrics and Intelligent Laboratory Systems, 58(2), 109–130.
-
Rasmussen, C. E. & Williams, C. K. I. (2006). Gaussian Processes for Machine Learning. MIT Press. SciPy Latin-hypercube sampling.
-
Jones, D. R., Schonlau, M. & Welch, W. J. (1998). Efficient Global Optimization of Expensive Black-Box Functions. Journal of Global Optimization, 13, 455–492. Expected-improvement acquisition.
-
xraylib. X-ray interaction data and documentation. Used for characteristic line energies and relative rates.
-
RosettaSciIO community. Bruker detector-calibration discussion, issue #516. File-format and per-detector energy calibration notes.
-
NIST/SEMATECH. Multiple responses: the desirability approach. Engineering preference directions and targets; GreyTheory uses separately documented arithmetic GRA grades, not the geometric-mean desirability function.
-
Pillow. Image.open and ImageOps. File loading, EXIF orientation, and aspect-preserving padding; installed with the
imagesextra.
Metadata
Release files for greytheory 2.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| greytheory-2.0.1.tar.gz | 1.3 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| greytheory-2.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.5 MB
Release files / greytheory-2.0.1.tar.gz
| Download URL | greytheory-2.0.1.tar.gz |
|---|---|
| Size | 1.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
808115130ce9f150ed73c11b5a30614ee609f6524ab1119b0b7245908b3a86c5
|
|
BLAKE2b-256 checksum How to use checksums |
0cc94e61e84683266a31906d309d7b75870058a235d1e5c2f5537ad26503c9d2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logRelease files / greytheory-2.0.1-py3-none-any.whl
| Download URL | greytheory-2.0.1-py3-none-any.whl |
|---|---|
| Size | 1.2 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dd1932d24f3974588e975ab8bc290973d5b7d34c21d4733a275cdcc59a555bb9
|
|
BLAKE2b-256 checksum How to use checksums |
0a8886bd76c59d2c391345d19c51249372313c026a56759d889a930f30b440a4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logSecurity for greytheory 2.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for greytheory-2.0.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.