triplexanalytics 0.2.2__tar.gz → 0.4.0__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.
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/Cargo.lock +3 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/PKG-INFO +9 -2
- {triplexanalytics-0.2.2/packages/triplex-python → triplexanalytics-0.4.0}/README.md +8 -1
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/bucket.rs +4 -62
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/lib.rs +16 -3
- triplexanalytics-0.4.0/crates/triplex-anonymizer/src/recipe.rs +584 -0
- triplexanalytics-0.4.0/crates/triplex-anonymizer/tests/salt.rs +61 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-datasetcard/Cargo.toml +2 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-datasetcard/README.md +2 -0
- triplexanalytics-0.4.0/crates/triplex-datasetcard/src/generalize.rs +67 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-datasetcard/src/lib.rs +26 -8
- triplexanalytics-0.4.0/crates/triplex-datasetcard/src/report.rs +323 -0
- triplexanalytics-0.4.0/crates/triplex-datasetcard/tests/report.rs +197 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-schemacast/src/contract.rs +36 -36
- triplexanalytics-0.4.0/crates/triplex-schemacast/tests/contract_yaml.rs +34 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0/packages/triplex-python}/README.md +8 -1
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/pyproject.toml +1 -1
- triplexanalytics-0.2.2/crates/triplex-anonymizer/src/recipe.rs +0 -264
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/Cargo.toml +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/Cargo.toml +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/README.md +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/fpe.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/mapping.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/mechanism.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/partial_mask.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/bayesnet/cells.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/bayesnet/dp.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/bayesnet/mod.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/bayesnet/model.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/bayesnet/score.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/bayesnet/structure.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/copula.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/dp/accountant.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/dp/histogram.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/dp/kendall.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/dp/mod.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/dp/noise.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/dp/report_html.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/dp/suggest.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/metrics.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/mod.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/numeric.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/report_html.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/report_script.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/synthesize/sketch.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/src/tokenize.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/tests/bayesnet.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/tests/dp.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/tests/sketch.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/tests/synthesis.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-anonymizer/tests/synthesis_bytes.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-core/Cargo.toml +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-core/README.md +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-core/src/error.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-core/src/lib.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-core/src/numeric.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-datasetcard/examples/demo.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-datasetcard/src/ebm.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-datasetcard/src/suda.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/Cargo.toml +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/README.md +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/arrow_ipc_sink.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/arrow_ipc_source.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/csv_sink.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/csv_source.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/delimited.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/format.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/jsonl_sink.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/jsonl_source.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/mod.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/parquet_sink.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/parquet_source.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/tsv_sink.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/formats/tsv_source.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/lib.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/shared/distribution.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/shared/mod.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/shared/sample.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-io/src/shared/statistics.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-schemacast/Cargo.toml +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-schemacast/README.md +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-schemacast/src/convert.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-schemacast/src/lib.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/crates/triplex-schemacast/src/semantic.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/packages/triplex-python/Cargo.toml +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/packages/triplex-python/src/lib.rs +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/packages/triplex-python/tests/test_triplex.py +0 -0
- {triplexanalytics-0.2.2 → triplexanalytics-0.4.0}/python/triplex/__init__.py +0 -0
|
@@ -1389,6 +1389,7 @@ version = "0.0.0"
|
|
|
1389
1389
|
dependencies = [
|
|
1390
1390
|
"serde_json",
|
|
1391
1391
|
"triplex-anonymizer",
|
|
1392
|
+
"triplex-datasetcard",
|
|
1392
1393
|
"triplex-driftguard",
|
|
1393
1394
|
"triplex-modelcard",
|
|
1394
1395
|
"triplex-schemacast",
|
|
@@ -1425,6 +1426,8 @@ dependencies = [
|
|
|
1425
1426
|
"serde",
|
|
1426
1427
|
"serde_json",
|
|
1427
1428
|
"triplex-core",
|
|
1429
|
+
"triplex-io",
|
|
1430
|
+
"triplex-schemacast",
|
|
1428
1431
|
]
|
|
1429
1432
|
|
|
1430
1433
|
[[package]]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: triplexanalytics
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -30,7 +30,14 @@ Triplex Analytics in plain Python: infer and check schema contracts, anonymize a
|
|
|
30
30
|
synthetic data. No notebook, no browser, no server. The engine is the same native code the
|
|
31
31
|
`triplex` command line runs.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
```sh
|
|
34
|
+
pip install triplexanalytics
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The distribution is called `triplexanalytics`; the import is `import triplex`. Wheels cover Linux
|
|
38
|
+
(x86_64 and aarch64), macOS and Windows, for Python 3.9 and later.
|
|
39
|
+
|
|
40
|
+
To build from a source checkout instead (needs the Rust toolchain):
|
|
34
41
|
|
|
35
42
|
```sh
|
|
36
43
|
cd packages/triplex-python
|
|
@@ -4,7 +4,14 @@ Triplex Analytics in plain Python: infer and check schema contracts, anonymize a
|
|
|
4
4
|
synthetic data. No notebook, no browser, no server. The engine is the same native code the
|
|
5
5
|
`triplex` command line runs.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```sh
|
|
8
|
+
pip install triplexanalytics
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The distribution is called `triplexanalytics`; the import is `import triplex`. Wheels cover Linux
|
|
12
|
+
(x86_64 and aarch64), macOS and Windows, for Python 3.9 and later.
|
|
13
|
+
|
|
14
|
+
To build from a source checkout instead (needs the Rust toolchain):
|
|
8
15
|
|
|
9
16
|
```sh
|
|
10
17
|
cd packages/triplex-python
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
//! Numeric generalization/bucketing — the Tier 1 k-anonymity
|
|
2
|
-
//! mechanism. The
|
|
2
|
+
//! mechanism. The bucketing itself (`bucket_value`, `bucket_numeric`) lives in
|
|
3
|
+
//! `triplex-datasetcard`, so masking and risk scoring generalize a number the
|
|
4
|
+
//! same way; this module wraps it as a masking mechanism. The named risk models a
|
|
3
5
|
//! bucketed column can be checked against (`RiskModel`/`evaluate_risk_model`
|
|
4
6
|
//! and friends) live in `triplex-datasetcard`,
|
|
5
7
|
//! since scoring risk needs to run independently of any recipe, before a
|
|
6
8
|
//! masking mechanism is even chosen.
|
|
7
9
|
|
|
8
10
|
use crate::mechanism::Mechanism;
|
|
11
|
+
use triplex_datasetcard::bucket_value;
|
|
9
12
|
|
|
10
13
|
pub(crate) struct Bucketer {
|
|
11
14
|
width: u64,
|
|
@@ -25,37 +28,6 @@ impl Mechanism for Bucketer {
|
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
/// Replaces a numeric value with the `"lo-hi"` range it falls in, e.g.
|
|
29
|
-
/// width 10 turns `"23"` into `"20-29"`. A value that isn't a whole number
|
|
30
|
-
/// has nothing to generalize and passes through unchanged.
|
|
31
|
-
fn bucket_number(width: u64, n: i64) -> String {
|
|
32
|
-
let width = width.max(1) as i64;
|
|
33
|
-
let lo = n.div_euclid(width) * width;
|
|
34
|
-
let hi = lo + width - 1;
|
|
35
|
-
format!("{lo}-{hi}")
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
pub fn bucket_value(width: u64, value: &str) -> String {
|
|
39
|
-
let Ok(n) = value.parse::<i64>() else {
|
|
40
|
-
return value.to_string();
|
|
41
|
-
};
|
|
42
|
-
bucket_number(width, n)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/// Same bucketing as `bucket_value`, for a caller that already has a typed
|
|
46
|
-
/// numeric value instead of a string to parse (e.g. `apps/triplex-web`'s
|
|
47
|
-
/// risk-scoring wasm exports, which take a typed-array of `f64` straight
|
|
48
|
-
/// from the browser instead of a JSON string of stringified cells).
|
|
49
|
-
/// `NaN` represents a
|
|
50
|
-
/// missing/non-numeric cell, passed through as an empty string, the same
|
|
51
|
-
/// convention `bucket_value` uses for a value that fails to parse.
|
|
52
|
-
pub fn bucket_numeric(width: u64, value: f64) -> String {
|
|
53
|
-
if value.is_nan() {
|
|
54
|
-
return String::new();
|
|
55
|
-
}
|
|
56
|
-
bucket_number(width, value as i64)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
31
|
/// Generalizes a string value by keeping only its first `width` characters
|
|
60
32
|
/// (Unicode scalar values, not bytes, so a multi-byte character is never
|
|
61
33
|
/// split mid-codepoint) — the "automatic" string generalization ARX ships
|
|
@@ -82,36 +54,6 @@ pub fn generalize_string_prefix(width: usize, value: &str) -> String {
|
|
|
82
54
|
mod tests {
|
|
83
55
|
use super::*;
|
|
84
56
|
|
|
85
|
-
#[test]
|
|
86
|
-
fn buckets_a_number_into_its_width_sized_range() {
|
|
87
|
-
assert_eq!(bucket_value(10, "23"), "20-29");
|
|
88
|
-
assert_eq!(bucket_value(10, "20"), "20-29");
|
|
89
|
-
assert_eq!(bucket_value(10, "29"), "20-29");
|
|
90
|
-
assert_eq!(bucket_value(10, "30"), "30-39");
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
#[test]
|
|
94
|
-
fn negative_numbers_bucket_toward_negative_infinity_not_toward_zero() {
|
|
95
|
-
// div_euclid, not integer truncation: -1 must land in "-10--1", not "0-9".
|
|
96
|
-
assert_eq!(bucket_value(10, "-1"), "-10--1");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
#[test]
|
|
100
|
-
fn a_non_numeric_value_passes_through_unchanged() {
|
|
101
|
-
assert_eq!(bucket_value(10, "n/a"), "n/a");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
#[test]
|
|
105
|
-
fn bucket_numeric_matches_bucket_value_for_the_same_number() {
|
|
106
|
-
assert_eq!(bucket_numeric(10, 23.0), bucket_value(10, "23"));
|
|
107
|
-
assert_eq!(bucket_numeric(10, -1.0), bucket_value(10, "-1"));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
#[test]
|
|
111
|
-
fn bucket_numeric_treats_nan_as_the_missing_value_case() {
|
|
112
|
-
assert_eq!(bucket_numeric(10, f64::NAN), "");
|
|
113
|
-
}
|
|
114
|
-
|
|
115
57
|
#[test]
|
|
116
58
|
fn generalize_string_prefix_keeps_only_the_leading_characters() {
|
|
117
59
|
assert_eq!(generalize_string_prefix(4, "3512AD"), "3512");
|
|
@@ -14,14 +14,16 @@ mod recipe;
|
|
|
14
14
|
pub mod synthesize;
|
|
15
15
|
mod tokenize;
|
|
16
16
|
|
|
17
|
-
pub use bucket::
|
|
17
|
+
pub use bucket::generalize_string_prefix;
|
|
18
18
|
pub use fpe::fpe_mask;
|
|
19
19
|
pub use mapping::{decrypt_mapping, encrypt_mapping, EncryptedMapping, MappingEntry};
|
|
20
20
|
pub use partial_mask::partial_mask;
|
|
21
21
|
pub use recipe::{
|
|
22
|
-
load_recipe, verify_recipe_well_formed, AnonymizeColumn, AnonymizeMethod,
|
|
22
|
+
load_recipe, recipe_from_yaml, verify_recipe_well_formed, AnonymizeColumn, AnonymizeMethod,
|
|
23
|
+
AnonymizeRecipe, RECIPE_VERSION,
|
|
23
24
|
};
|
|
24
25
|
pub use tokenize::hmac_token;
|
|
26
|
+
pub use triplex_datasetcard::{bucket_numeric, bucket_value};
|
|
25
27
|
// Risk-model scoring moved to `triplex-datasetcard` — kept
|
|
26
28
|
// re-exported here so nothing that already does
|
|
27
29
|
// `use triplex_anonymizer::{RiskModel, ...}` breaks.
|
|
@@ -169,7 +171,12 @@ fn resolve_columns<'a>(
|
|
|
169
171
|
drop_indices.insert(header_index);
|
|
170
172
|
continue;
|
|
171
173
|
}
|
|
172
|
-
|
|
174
|
+
// Only a keyed method needs a salt; the others give the same output for anyone.
|
|
175
|
+
let salt = if declared.method.is_keyed() {
|
|
176
|
+
salt_for(declared)?
|
|
177
|
+
} else {
|
|
178
|
+
Vec::new()
|
|
179
|
+
};
|
|
173
180
|
resolved.push(ResolvedColumn {
|
|
174
181
|
header_index,
|
|
175
182
|
declared,
|
|
@@ -183,6 +190,12 @@ fn resolve_columns<'a>(
|
|
|
183
190
|
/// itself is never a CLI argument, so it never lands in shell history or a
|
|
184
191
|
/// process listing.
|
|
185
192
|
fn salt_from_env(declared: &AnonymizeColumn) -> Result<Vec<u8>> {
|
|
193
|
+
if declared.salt_env.is_empty() {
|
|
194
|
+
return Err(Error::Validation(format!(
|
|
195
|
+
"column {:?} uses {:?}, which needs salt_env: the environment variable holding its salt",
|
|
196
|
+
declared.name, declared.method
|
|
197
|
+
)));
|
|
198
|
+
}
|
|
186
199
|
std::env::var(&declared.salt_env)
|
|
187
200
|
.map(String::into_bytes)
|
|
188
201
|
.map_err(|_| Error::MissingEnvVar(declared.salt_env.clone()))
|