Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Data quality validation skill using Great Expectations for schema validation, expectation suites, data documentation, and automated data quality checks in ML pipelines.
.claude/skills/a5c-ai-great-expectations-validator/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 84% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 168% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 125% | 0% |
| case-04 | ✓→✗ | ▼ Worse | 58% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 92% | 0% |
Validate data quality using Great Expectations for comprehensive data testing, documentation, and quality monitoring.
This skill provides capabilities for data quality validation using Great Expectations (GX), the leading open-source library for data quality. It enables creation and execution of expectation suites, data documentation generation, and integration with ML pipelines.
bashpip install great_expectations>=0.18.0
bash# Database connectors pip install great_expectations[sqlalchemy] # Cloud storage pip install great_expectations[s3] # AWS pip install great_expectations[gcs] # GCP pip install great_expectations[azure] # Azure # Spark support pip install great_expectations[spark]
bash# Initialize GX project great_expectations init # Creates: # great_expectations/ # ├── great_expectations.yml # ├── expectations/ # ├── checkpoints/ # ├── plugins/ # └── uncommitted/
pythonimport great_expectations as gx # Initialize context context = gx.get_context() # Add datasource datasource = context.sources.add_pandas("my_datasource") data_asset = datasource.add_csv_asset("customers", filepath_or_buffer="customers.csv") # Create batch request batch_request = data_asset.build_batch_request() # Create expectation suite with profiler expectation_suite = context.add_or_update_expectation_suite("customer_suite") validator = context.get_validator( batch_request=batch_request, expectation_suite_name="customer_suite" ) # Profile and generate expectations validator.expect_column_to_exist("customer_id") validator.expect_column_values_to_be_unique("customer_id") validator.expect_column_values_to_not_be_null("customer_id") validator.expect_column_values_to_be_between("age", min_value=0, max_value=120) validator.expect_column_values_to_be_in_set("status", ["active", "inactive", "pending"]) validator.expect_column_values_to_match_regex("email", r"^[\w\.-]+@[\w\.-]+\.\w+$") # Save suite validator.save_expectation_suite(discard_failed_expectations=False)
pythonimport great_expectations as gx context = gx.get_context() # Create checkpoint checkpoint = context.add_or_update_checkpoint( name="customer_checkpoint", validations=[ { "batch_request": { "datasource_name": "my_datasource", "data_asset_name": "customers" }, "expectation_suite_name": "customer_suite" } ], action_list=[ { "name": "store_validation_result", "action": {"class_name": "StoreValidationResultAction"} }, { "name": "update_data_docs", "action": {"class_name": "UpdateDataDocsAction"} } ] ) # Run checkpoint result = checkpoint.run() # Check results if result.success: print("Validation passed!") else: print("Validation failed!") for validation_result in result.run_results.values(): for result in validation_result.results: if not result.success: print(f"Failed: {result.expectation_config.expectation_type}")
python# Column existence and types validator.expect_column_to_exist("column_name") validator.expect_column_values_to_be_of_type("column_name", "int64") validator.expect_table_column_count_to_equal(10) # Null handling validator.expect_column_values_to_not_be_null("column_name") validator.expect_column_values_to_be_null("deprecated_column") # Uniqueness validator.expect_column_values_to_be_unique("id_column") validator.expect_compound_columns_to_be_unique(["col1", "col2"]) # Value ranges validator.expect_column_values_to_be_between("age", min_value=0, max_value=120) validator.expect_column_min_to_be_between("score", min_value=0) validator.expect_column_max_to_be_between("score", max_value=100) # Set membership validator.expect_column_values_to_be_in_set("status", ["A", "B", "C"]) validator.expect_column_distinct_values_to_be_in_set("category", ["cat1", "cat2"]) # String patterns validator.expect_column_values_to_match_regex("email", r"^[\w\.-]+@[\w\.-]+\.\w+$") validator.expect_column_value_lengths_to_be_between("code", min_value=5, max_value=10) # Statistical validator.expect_column_mean_to_be_between("value", min_value=50, max_value=100) validator.expect_column_stdev_to_be_between("value", min_value=0, max_value=20) validator.expect_column_proportion_of_unique_values_to_be_between("id", min_value=0.9)
javascriptconst dataValidationTask = defineTask({ name: 'great-expectations-validation', description: 'Validate data quality using Great Expectations', inputs: { dataPath: { type: 'string', required: true }, expectationSuiteName: { type: 'string', required: true }, checkpointName: { type: 'string' }, failOnError: { type: 'boolean', default: true } }, outputs: { success: { type: 'boolean' }, validationResults: { type: 'object' }, failedExpectations: { type: 'array' }, dataDocsUrl: { type: 'string' } }, async run(inputs, taskCtx) { return { kind: 'skill', title: `Validate data: ${inputs.expectationSuiteName}`, skill: { name: 'great-expectations-validator', context: { operation: 'validate', dataPath: inputs.dataPath, expectationSuiteName: inputs.expectationSuiteName, checkpointName: inputs.checkpointName, failOnError: inputs.failOnError } }, io: { inputJsonPath: `tasks/${taskCtx.effectId}/input.json`, outputJsonPath: `tasks/${taskCtx.effectId}/result.json` } }; } });
json{ "mcpServers": { "great-expectations": { "command": "uvx", "args": ["gx-mcp-server"], "env": { "GX_CONTEXT_ROOT": "./great_expectations" } } } }
gx_list_datasources - List configured datasourcesgx_list_expectation_suites - List expectation suitesgx_run_checkpoint - Execute a checkpointgx_validate_data - Validate data against suitegx_get_validation_results - Retrieve validation resultspythondef validate_training_data(df, suite_name="training_data_suite"): """Validate training data before model training.""" context = gx.get_context() # Add dataframe as datasource datasource = context.sources.add_pandas("training_data") data_asset = datasource.add_dataframe_asset("df") batch_request = data_asset.build_batch_request(dataframe=df) # Validate checkpoint = context.add_or_update_checkpoint( name="training_validation", validations=[{ "batch_request": batch_request, "expectation_suite_name": suite_name }] ) result = checkpoint.run() if not result.success: failed = [r for r in result.run_results.values() for r in r.results if not r.success] raise ValueError(f"Training data validation failed: {len(failed)} expectations failed") return True
python# Expectations for ML features validator.expect_column_values_to_not_be_null("feature_1", mostly=0.95) validator.expect_column_values_to_be_between("feature_1", min_value=-3, max_value=3) # Standard scaled validator.expect_column_proportion_of_unique_values_to_be_between("categorical_feature", min_value=0.001) validator.expect_column_kl_divergence_to_be_less_than("feature_1", partition_object=reference_distribution, threshold=0.1)
mostly=0.95| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 13,986 | 8,455 | -40% | 1 | 1 | 0% | 3,132 | 4,573 | +46% | 0 | 0 | — |
case-02 | fail→fail | 8,940 | 6,350 | -29% | 1 | 1 | 0% | 1,741 | 3,882 | +123% | 0 | 0 | — |
case-03 | pass→pass | 10,441 | 8,334 | -20% | 1 | 1 | 0% | 1,997 | 3,838 | +92% | 0 | 0 | — |
case-04 | pass→fail | 13,509 | 7,331 | -46% | 1 | 1 | 0% | 2,647 | 4,170 | +58% | 0 | 0 | — |
case-05 | fail→pass | 11,501 | 7,433 | -35% | 1 | 1 | 0% | 2,305 | 4,250 | +84% | 0 | 0 | — |
case-06 | pass→pass | 2,976 | 3,110 | +5% | 1 | 1 | 0% | 681 | 3,243 | +376% | 0 | 0 | — |
case-07 | pass→pass | 9,762 | 5,529 | -43% | 1 | 1 | 0% | 2,233 | 3,938 | +76% | 0 | 0 | — |
case-08 | fail→pass | 5,489 | 2,280 | -58% | 1 | 1 | 0% | 1,148 | 3,079 | +168% | 0 | 0 | — |
case-09 | pass→pass | 3,694 | 3,585 | -3% | 1 | 1 | 0% | 706 | 3,317 | +370% | 0 | 0 | — |
case-10 | pass→pass | 5,812 | 3,614 | -38% | 1 | 1 | 0% | 1,249 | 3,308 | +165% | 0 | 0 | — |
case-11 | pass→pass | 7,338 | 3,303 | -55% | 1 | 1 | 0% | 1,424 | 3,214 | +126% | 0 | 0 | — |
case-12 | fail→pass | 7,251 | 2,482 | -66% | 1 | 1 | 0% | 1,333 | 3,002 | +125% | 0 | 0 | — |
case-13 | pass→pass | 2,598 | 2,553 | -2% | 1 | 1 | 0% | 454 | 3,059 | +574% | 0 | 0 | — |
case-14 | pass→pass | 4,046 | 3,369 | -17% | 1 | 1 | 0% | 798 | 3,267 | +309% | 0 | 0 | — |
case-15 | fail→fail | 10,282 | 7,511 | -27% | 1 | 1 | 0% | 1,748 | 4,018 | +130% | 0 | 0 | — |
case-16 | pass→pass | 3,312 | 3,917 | +18% | 1 | 1 | 0% | 628 | 3,388 | +439% | 0 | 0 | — |
case-17 | pass→pass | 8,125 | 5,674 | -30% | 1 | 1 | 0% | 1,452 | 3,687 | +154% | 0 | 0 | — |
case-18 | pass→pass | 6,118 | 2,755 | -55% | 1 | 1 | 0% | 1,160 | 2,904 | +150% | 0 | 0 | — |
case-19 | pass→pass | 3,477 | 3,513 | +1% | 1 | 1 | 0% | 578 | 3,210 | +455% | 0 | 0 | — |
case-20 | pass→pass | 5,941 | 4,597 | -23% | 1 | 1 | 0% | 1,115 | 3,449 | +209% | 0 | 0 | — |
case-21 | pass→pass | 9,820 | 6,903 | -30% | 1 | 1 | 0% | 1,945 | 3,991 | +105% | 0 | 0 | — |
case-22 | pass→pass | 5,474 | 3,131 | -43% | 1 | 1 | 0% | 936 | 3,176 | +239% | 0 | 0 | — |
case-23 | pass→pass | 8,162 | 6,534 | -20% | 1 | 1 | 0% | 1,503 | 3,808 | +153% | 0 | 0 | — |
case-24 | pass→pass | 14,774 | 12,564 | -15% | 1 | 1 | 0% | 3,017 | 5,332 | +77% | 0 | 0 | — |
case-25 | pass→pass | 11,960 | 13,443 | +12% | 1 | 1 | 0% | 2,835 | 5,309 | +87% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted. The headline lift of +8 percentage points is the difference between those two pass rates over the 25 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.