validmind 2.1.1__py3-none-any.whl → 2.2.4__py3-none-any.whl
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.
- validmind/__version__.py +1 -1
- validmind/ai.py +72 -49
- validmind/api_client.py +42 -16
- validmind/client.py +68 -25
- validmind/datasets/llm/rag/__init__.py +11 -0
- validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_1.csv +30 -0
- validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_2.csv +30 -0
- validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_3.csv +53 -0
- validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_4.csv +53 -0
- validmind/datasets/llm/rag/datasets/rfp_existing_questions_client_5.csv +53 -0
- validmind/datasets/llm/rag/rfp.py +41 -0
- validmind/errors.py +1 -1
- validmind/html_templates/__init__.py +0 -0
- validmind/html_templates/content_blocks.py +89 -14
- validmind/models/__init__.py +7 -4
- validmind/models/foundation.py +8 -34
- validmind/models/function.py +51 -0
- validmind/models/huggingface.py +16 -46
- validmind/models/metadata.py +42 -0
- validmind/models/pipeline.py +66 -0
- validmind/models/pytorch.py +8 -42
- validmind/models/r_model.py +33 -82
- validmind/models/sklearn.py +39 -38
- validmind/template.py +8 -26
- validmind/tests/__init__.py +43 -20
- validmind/tests/data_validation/ANOVAOneWayTable.py +1 -1
- validmind/tests/data_validation/ChiSquaredFeaturesTable.py +1 -1
- validmind/tests/data_validation/DescriptiveStatistics.py +2 -4
- validmind/tests/data_validation/Duplicates.py +1 -1
- validmind/tests/data_validation/IsolationForestOutliers.py +2 -2
- validmind/tests/data_validation/LaggedCorrelationHeatmap.py +1 -1
- validmind/tests/data_validation/TargetRateBarPlots.py +1 -1
- validmind/tests/data_validation/nlp/LanguageDetection.py +59 -0
- validmind/tests/data_validation/nlp/PolarityAndSubjectivity.py +48 -0
- validmind/tests/data_validation/nlp/Punctuations.py +11 -12
- validmind/tests/data_validation/nlp/Sentiment.py +57 -0
- validmind/tests/data_validation/nlp/Toxicity.py +45 -0
- validmind/tests/decorator.py +12 -7
- validmind/tests/model_validation/BertScore.py +100 -98
- validmind/tests/model_validation/BleuScore.py +93 -64
- validmind/tests/model_validation/ContextualRecall.py +74 -91
- validmind/tests/model_validation/MeteorScore.py +86 -74
- validmind/tests/model_validation/RegardScore.py +103 -121
- validmind/tests/model_validation/RougeScore.py +118 -0
- validmind/tests/model_validation/TokenDisparity.py +84 -121
- validmind/tests/model_validation/ToxicityScore.py +109 -123
- validmind/tests/model_validation/embeddings/CosineSimilarityComparison.py +96 -0
- validmind/tests/model_validation/embeddings/CosineSimilarityHeatmap.py +71 -0
- validmind/tests/model_validation/embeddings/EuclideanDistanceComparison.py +92 -0
- validmind/tests/model_validation/embeddings/EuclideanDistanceHeatmap.py +69 -0
- validmind/tests/model_validation/embeddings/PCAComponentsPairwisePlots.py +78 -0
- validmind/tests/model_validation/embeddings/StabilityAnalysis.py +35 -23
- validmind/tests/model_validation/embeddings/StabilityAnalysisKeyword.py +3 -0
- validmind/tests/model_validation/embeddings/StabilityAnalysisRandomNoise.py +7 -1
- validmind/tests/model_validation/embeddings/StabilityAnalysisSynonyms.py +3 -0
- validmind/tests/model_validation/embeddings/StabilityAnalysisTranslation.py +3 -0
- validmind/tests/model_validation/embeddings/TSNEComponentsPairwisePlots.py +99 -0
- validmind/tests/model_validation/ragas/AnswerCorrectness.py +131 -0
- validmind/tests/model_validation/ragas/AnswerRelevance.py +134 -0
- validmind/tests/model_validation/ragas/AnswerSimilarity.py +119 -0
- validmind/tests/model_validation/ragas/AspectCritique.py +167 -0
- validmind/tests/model_validation/ragas/ContextEntityRecall.py +133 -0
- validmind/tests/model_validation/ragas/ContextPrecision.py +123 -0
- validmind/tests/model_validation/ragas/ContextRecall.py +123 -0
- validmind/tests/model_validation/ragas/ContextRelevancy.py +114 -0
- validmind/tests/model_validation/ragas/Faithfulness.py +119 -0
- validmind/tests/model_validation/ragas/utils.py +66 -0
- validmind/tests/model_validation/sklearn/OverfitDiagnosis.py +3 -7
- validmind/tests/model_validation/sklearn/PermutationFeatureImportance.py +8 -9
- validmind/tests/model_validation/sklearn/PopulationStabilityIndex.py +5 -10
- validmind/tests/model_validation/sklearn/PrecisionRecallCurve.py +3 -2
- validmind/tests/model_validation/sklearn/ROCCurve.py +2 -1
- validmind/tests/model_validation/sklearn/RegressionR2Square.py +1 -1
- validmind/tests/model_validation/sklearn/RobustnessDiagnosis.py +2 -3
- validmind/tests/model_validation/sklearn/SHAPGlobalImportance.py +7 -11
- validmind/tests/model_validation/sklearn/WeakspotsDiagnosis.py +3 -4
- validmind/tests/model_validation/statsmodels/RegressionModelForecastPlot.py +1 -1
- validmind/tests/model_validation/statsmodels/RegressionModelForecastPlotLevels.py +1 -1
- validmind/tests/model_validation/statsmodels/RegressionModelInsampleComparison.py +1 -1
- validmind/tests/model_validation/statsmodels/RegressionModelOutsampleComparison.py +1 -1
- validmind/tests/model_validation/statsmodels/RegressionModelSummary.py +1 -1
- validmind/tests/model_validation/statsmodels/RegressionModelsCoeffs.py +1 -1
- validmind/tests/model_validation/statsmodels/RegressionModelsPerformance.py +1 -1
- validmind/tests/model_validation/statsmodels/ScorecardHistogram.py +5 -6
- validmind/unit_metrics/__init__.py +26 -49
- validmind/unit_metrics/composite.py +13 -7
- validmind/unit_metrics/regression/sklearn/AdjustedRSquaredScore.py +1 -1
- validmind/utils.py +99 -6
- validmind/vm_models/__init__.py +1 -1
- validmind/vm_models/dataset/__init__.py +7 -0
- validmind/vm_models/dataset/dataset.py +560 -0
- validmind/vm_models/dataset/utils.py +146 -0
- validmind/vm_models/model.py +97 -72
- validmind/vm_models/test/metric.py +9 -24
- validmind/vm_models/test/result_wrapper.py +124 -28
- validmind/vm_models/test/threshold_test.py +10 -28
- validmind/vm_models/test_context.py +1 -1
- validmind/vm_models/test_suite/summary.py +3 -4
- {validmind-2.1.1.dist-info → validmind-2.2.4.dist-info}/METADATA +5 -3
- {validmind-2.1.1.dist-info → validmind-2.2.4.dist-info}/RECORD +103 -78
- validmind/models/catboost.py +0 -33
- validmind/models/statsmodels.py +0 -50
- validmind/models/xgboost.py +0 -30
- validmind/tests/model_validation/BertScoreAggregate.py +0 -90
- validmind/tests/model_validation/RegardHistogram.py +0 -148
- validmind/tests/model_validation/RougeMetrics.py +0 -147
- validmind/tests/model_validation/RougeMetricsAggregate.py +0 -133
- validmind/tests/model_validation/SelfCheckNLIScore.py +0 -112
- validmind/tests/model_validation/ToxicityHistogram.py +0 -136
- validmind/vm_models/dataset.py +0 -1303
- {validmind-2.1.1.dist-info → validmind-2.2.4.dist-info}/LICENSE +0 -0
- {validmind-2.1.1.dist-info → validmind-2.2.4.dist-info}/WHEEL +0 -0
- {validmind-2.1.1.dist-info → validmind-2.2.4.dist-info}/entry_points.txt +0 -0
validmind/template.py
CHANGED
@@ -2,20 +2,15 @@
|
|
2
2
|
# See the LICENSE file in the root of this repository for details.
|
3
3
|
# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
|
4
4
|
|
5
|
-
from pprint import pformat
|
6
|
-
|
7
|
-
import mistune
|
8
|
-
from IPython.display import display
|
9
5
|
from ipywidgets import HTML, Accordion, VBox
|
10
6
|
|
11
7
|
from .html_templates.content_blocks import (
|
12
8
|
failed_content_block_html,
|
13
9
|
non_test_content_block_html,
|
14
|
-
test_content_block_html,
|
15
10
|
)
|
16
11
|
from .logging import get_logger
|
17
12
|
from .tests import LoadTestError, describe_test
|
18
|
-
from .utils import is_notebook
|
13
|
+
from .utils import display, is_notebook
|
19
14
|
from .vm_models import TestSuite
|
20
15
|
|
21
16
|
logger = get_logger(__name__)
|
@@ -26,6 +21,7 @@ CONTENT_TYPE_MAP = {
|
|
26
21
|
"metadata_text": "Metadata Text",
|
27
22
|
"dynamic": "Dynamic Content",
|
28
23
|
"text": "Text",
|
24
|
+
"risk_assessment": "Risk Assessment",
|
29
25
|
}
|
30
26
|
|
31
27
|
|
@@ -66,29 +62,12 @@ def _create_content_widget(content):
|
|
66
62
|
)
|
67
63
|
|
68
64
|
try:
|
69
|
-
|
65
|
+
test_html = describe_test(test_id=content["content_id"], show=False)
|
70
66
|
except LoadTestError:
|
71
67
|
return HTML(failed_content_block_html.format(test_id=content["content_id"]))
|
72
68
|
|
73
69
|
return Accordion(
|
74
|
-
children=[
|
75
|
-
HTML(
|
76
|
-
test_content_block_html.format(
|
77
|
-
title=test_deets["Name"],
|
78
|
-
description=mistune.html(test_deets["Description"]),
|
79
|
-
required_inputs=", ".join(
|
80
|
-
test_deets["Required Inputs"] or ["None"]
|
81
|
-
),
|
82
|
-
params_table="\n".join(
|
83
|
-
[
|
84
|
-
f"<tr><td>{param}</td><td>{pformat(value, indent=4)}</td></tr>"
|
85
|
-
for param, value in test_deets["Params"].items()
|
86
|
-
]
|
87
|
-
),
|
88
|
-
table_display="table" if test_deets["Params"] else "none",
|
89
|
-
)
|
90
|
-
)
|
91
|
-
],
|
70
|
+
children=[HTML(test_html)],
|
92
71
|
titles=[f"{content_type} Block: '{content['content_id']}'"],
|
93
72
|
)
|
94
73
|
|
@@ -117,7 +96,10 @@ def _create_sub_section_widget(sub_sections, section_number):
|
|
117
96
|
contents_widget,
|
118
97
|
)
|
119
98
|
else:
|
120
|
-
accordion.children = (
|
99
|
+
accordion.children = (
|
100
|
+
*accordion.children,
|
101
|
+
HTML("<p>Empty Section</p>"),
|
102
|
+
)
|
121
103
|
|
122
104
|
accordion.set_title(
|
123
105
|
i, f"{section_number}.{i + 1}. {section['title']} ('{section['id']}')"
|
validmind/tests/__init__.py
CHANGED
@@ -6,22 +6,29 @@
|
|
6
6
|
|
7
7
|
import importlib
|
8
8
|
import inspect
|
9
|
+
import json
|
9
10
|
import sys
|
10
11
|
from pathlib import Path
|
11
12
|
from pprint import pformat
|
12
13
|
from typing import Dict
|
14
|
+
from uuid import uuid4
|
13
15
|
|
14
|
-
import mistune
|
15
16
|
import pandas as pd
|
16
|
-
from
|
17
|
-
from ipywidgets import HTML
|
17
|
+
from ipywidgets import HTML, Accordion
|
18
18
|
|
19
19
|
from ..errors import LoadTestError
|
20
20
|
from ..html_templates.content_blocks import test_content_block_html
|
21
21
|
from ..logging import get_logger
|
22
22
|
from ..unit_metrics import run_metric
|
23
23
|
from ..unit_metrics.composite import load_composite_metric
|
24
|
-
from ..utils import
|
24
|
+
from ..utils import (
|
25
|
+
NumpyEncoder,
|
26
|
+
display,
|
27
|
+
format_dataframe,
|
28
|
+
fuzzy_match,
|
29
|
+
md_to_html,
|
30
|
+
test_id_to_name,
|
31
|
+
)
|
25
32
|
from ..vm_models import TestContext, TestInput
|
26
33
|
from .decorator import metric, tags, tasks
|
27
34
|
from .test_providers import LocalTestProvider, TestProvider
|
@@ -75,10 +82,12 @@ def _pretty_list_tests(tests, truncate=True):
|
|
75
82
|
|
76
83
|
table = [
|
77
84
|
{
|
78
|
-
"
|
85
|
+
"ID": test_id,
|
79
86
|
"Name": test_id_to_name(test_id),
|
87
|
+
"Test Type": __test_classes[test_id].test_type,
|
80
88
|
"Description": _test_description(__test_classes[test_id], truncate),
|
81
|
-
"
|
89
|
+
"Required Inputs": __test_classes[test_id].required_inputs,
|
90
|
+
"Params": __test_classes[test_id].default_params or {},
|
82
91
|
}
|
83
92
|
for test_id in tests
|
84
93
|
]
|
@@ -339,7 +348,7 @@ def load_test(test_id: str, reload=False):
|
|
339
348
|
return test
|
340
349
|
|
341
350
|
|
342
|
-
def describe_test(test_id: str = None, raw: bool = False):
|
351
|
+
def describe_test(test_id: str = None, raw: bool = False, show: bool = True):
|
343
352
|
"""Get or show details about the test
|
344
353
|
|
345
354
|
This function can be used to see test details including the test name, description,
|
@@ -365,20 +374,34 @@ def describe_test(test_id: str = None, raw: bool = False):
|
|
365
374
|
if raw:
|
366
375
|
return details
|
367
376
|
|
377
|
+
html = test_content_block_html.format(
|
378
|
+
test_id=test_id,
|
379
|
+
uuid=str(uuid4()),
|
380
|
+
title=f'{details["Name"]}',
|
381
|
+
description=md_to_html(details["Description"].strip()),
|
382
|
+
required_inputs=", ".join(details["Required Inputs"] or ["None"]),
|
383
|
+
params_table="\n".join(
|
384
|
+
[
|
385
|
+
f"<tr><td>{param}</td><td>{pformat(value, indent=4)}</td></tr>"
|
386
|
+
for param, value in details["Params"].items()
|
387
|
+
]
|
388
|
+
),
|
389
|
+
table_display="table" if details["Params"] else "none",
|
390
|
+
example_inputs=json.dumps(
|
391
|
+
{name: f"my_vm_{name}" for name in (details["Required Inputs"] or [])},
|
392
|
+
indent=4,
|
393
|
+
),
|
394
|
+
example_params=json.dumps(details["Params"] or {}, indent=4, cls=NumpyEncoder),
|
395
|
+
instructions_display="block" if show else "none",
|
396
|
+
)
|
397
|
+
|
398
|
+
if not show:
|
399
|
+
return html
|
400
|
+
|
368
401
|
display(
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
description=mistune.html(details["Description"].strip()),
|
373
|
-
required_inputs=", ".join(details["Required Inputs"] or ["None"]),
|
374
|
-
params_table="\n".join(
|
375
|
-
[
|
376
|
-
f"<tr><td>{param}</td><td>{pformat(value, indent=4)}</td></tr>"
|
377
|
-
for param, value in details["Params"].items()
|
378
|
-
]
|
379
|
-
),
|
380
|
-
table_display="table" if details["Params"] else "none",
|
381
|
-
)
|
402
|
+
Accordion(
|
403
|
+
children=[HTML(html)],
|
404
|
+
titles=[f"Test Description: {details['Name']} ('{test_id}')"],
|
382
405
|
)
|
383
406
|
)
|
384
407
|
|
@@ -74,7 +74,7 @@ class ANOVAOneWayTable(Metric):
|
|
74
74
|
|
75
75
|
# Select all numerical features if none are specified
|
76
76
|
if features is None:
|
77
|
-
features = self.inputs.dataset.
|
77
|
+
features = self.inputs.dataset.feature_columns_numeric
|
78
78
|
|
79
79
|
anova_results = self.anova_numerical_features(features, p_threshold)
|
80
80
|
|
@@ -72,7 +72,7 @@ class ChiSquaredFeaturesTable(Metric):
|
|
72
72
|
|
73
73
|
# Ensure cat_features is provided
|
74
74
|
if not cat_features:
|
75
|
-
cat_features = self.inputs.dataset.
|
75
|
+
cat_features = self.inputs.dataset.feature_columns_categorical
|
76
76
|
|
77
77
|
df = self.inputs.dataset.df
|
78
78
|
|
@@ -116,10 +116,8 @@ class DescriptiveStatistics(Metric):
|
|
116
116
|
|
117
117
|
def run(self):
|
118
118
|
feature_columns = self.inputs.dataset.feature_columns
|
119
|
-
numerical_feature_columns = self.inputs.dataset.
|
120
|
-
categorical_feature_columns =
|
121
|
-
self.inputs.dataset.get_categorical_features_columns()
|
122
|
-
)
|
119
|
+
numerical_feature_columns = self.inputs.dataset.feature_columns_numeric
|
120
|
+
categorical_feature_columns = self.inputs.dataset.feature_columns_categorical
|
123
121
|
|
124
122
|
df = self.inputs.dataset.df[feature_columns]
|
125
123
|
|
@@ -84,7 +84,7 @@ class Duplicates(ThresholdTest):
|
|
84
84
|
if self.inputs.dataset.text_column:
|
85
85
|
columns = self.inputs.dataset.text_column
|
86
86
|
else:
|
87
|
-
columns = self.inputs.dataset.
|
87
|
+
columns = self.inputs.dataset.feature_columns
|
88
88
|
|
89
89
|
df = self.inputs.dataset.df[columns]
|
90
90
|
# Find duplicate rows
|
@@ -64,13 +64,13 @@ class IsolationForestOutliers(Metric):
|
|
64
64
|
|
65
65
|
def run(self):
|
66
66
|
if self.params["features_columns"] is None:
|
67
|
-
features_list = self.inputs.dataset.
|
67
|
+
features_list = self.inputs.dataset.feature_columns
|
68
68
|
else:
|
69
69
|
features_list = self.params["features_columns"]
|
70
70
|
|
71
71
|
# Check if all elements from features_list are present in the feature columns
|
72
72
|
all_present = all(
|
73
|
-
elem in self.inputs.dataset.
|
73
|
+
elem in self.inputs.dataset.feature_columns for elem in features_list
|
74
74
|
)
|
75
75
|
if not all_present:
|
76
76
|
raise ValueError(
|
@@ -115,7 +115,7 @@ class LaggedCorrelationHeatmap(Metric):
|
|
115
115
|
else:
|
116
116
|
target_col = self.inputs.dataset.target_column
|
117
117
|
|
118
|
-
independent_vars = list(self.inputs.dataset.
|
118
|
+
independent_vars = list(self.inputs.dataset.feature_columns)
|
119
119
|
num_lags = self.params.get("num_lags", 10)
|
120
120
|
|
121
121
|
if isinstance(target_col, list) and len(target_col) == 1:
|
@@ -57,7 +57,7 @@ class TargetRateBarPlots(Metric):
|
|
57
57
|
|
58
58
|
# Use all categorical features if columns is not specified, else use selected columns
|
59
59
|
if columns is None:
|
60
|
-
features = self.inputs.dataset.
|
60
|
+
features = self.inputs.dataset.feature_columns_categorical
|
61
61
|
else:
|
62
62
|
features = columns
|
63
63
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright © 2023-2024 ValidMind Inc. All rights reserved.
|
2
|
+
# See the LICENSE file in the root of this repository for details.
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
|
4
|
+
|
5
|
+
"""
|
6
|
+
Metrics functions for any Pandas-compatible datasets
|
7
|
+
"""
|
8
|
+
|
9
|
+
|
10
|
+
import plotly.express as px
|
11
|
+
from langdetect import LangDetectException, detect
|
12
|
+
|
13
|
+
from validmind import tags, tasks
|
14
|
+
|
15
|
+
|
16
|
+
@tags("nlp", "text_data", "visualization")
|
17
|
+
@tasks("text_classification", "text_summarization")
|
18
|
+
def LanguageDetection(dataset):
|
19
|
+
"""
|
20
|
+
Detects the language of each text entry in a dataset and visualizes the distribution of languages
|
21
|
+
as a histogram.
|
22
|
+
|
23
|
+
This method checks for a specified text column in the dataset's dataframe, uses a language detection
|
24
|
+
library to determine the language of each text entry, and returns a histogram plot of the language
|
25
|
+
distribution.
|
26
|
+
|
27
|
+
Args:
|
28
|
+
dataset (Dataset): A dataset object which must have a `df` attribute (a pandas DataFrame)
|
29
|
+
and a `text_column` attribute indicating the name of the column containing text. If the
|
30
|
+
`text_column` attribute is not set, a ValueError is raised.
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
plotly.graph_objs._figure.Figure: A Plotly histogram plot showing the distribution of detected
|
34
|
+
languages across the dataset's text entries.
|
35
|
+
|
36
|
+
Raises:
|
37
|
+
ValueError: If the `text_column` is not specified in the dataset object.
|
38
|
+
"""
|
39
|
+
# check text column
|
40
|
+
if not dataset.text_column:
|
41
|
+
raise ValueError("Please set text_column name in the Validmind Dataset object")
|
42
|
+
|
43
|
+
# Function to detect language
|
44
|
+
def detect_language(text):
|
45
|
+
try:
|
46
|
+
return detect(text)
|
47
|
+
except LangDetectException:
|
48
|
+
return "Unknown" # Return 'Unknown' if language detection fails
|
49
|
+
|
50
|
+
# Applying the language detection function to each text entry
|
51
|
+
languages = dataset.df[dataset.text_column].apply(detect_language)
|
52
|
+
fig = px.histogram(
|
53
|
+
languages,
|
54
|
+
x=languages,
|
55
|
+
title="Language Distribution",
|
56
|
+
labels={"x": "Language Codes"},
|
57
|
+
)
|
58
|
+
|
59
|
+
return fig
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright © 2023-2024 ValidMind Inc. All rights reserved.
|
2
|
+
# See the LICENSE file in the root of this repository for details.
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
|
4
|
+
|
5
|
+
|
6
|
+
import pandas as pd
|
7
|
+
import plotly.express as px
|
8
|
+
from textblob import TextBlob
|
9
|
+
|
10
|
+
from validmind import tags, tasks
|
11
|
+
|
12
|
+
|
13
|
+
@tags("data_validation")
|
14
|
+
@tasks("nlp")
|
15
|
+
def PolarityAndSubjectivity(dataset):
|
16
|
+
"""
|
17
|
+
Analyzes the polarity and subjectivity of text data within a dataset.
|
18
|
+
|
19
|
+
This method processes a dataset containing textual data to compute the polarity and
|
20
|
+
subjectivity scores using TextBlob, and returns a Plotly scatter plot visualizing
|
21
|
+
these scores.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
dataset (Dataset): A dataset object which must have a `df` attribute (a pandas DataFrame)
|
25
|
+
and a `text_column` attribute indicating the name of the column containing text.
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
plotly.graph_objs._figure.Figure: A Plotly scatter plot of polarity vs subjectivity.
|
29
|
+
"""
|
30
|
+
# Function to calculate sentiment and subjectivity
|
31
|
+
def analyze_sentiment(text):
|
32
|
+
analysis = TextBlob(text)
|
33
|
+
return analysis.sentiment.polarity, analysis.sentiment.subjectivity
|
34
|
+
|
35
|
+
data = pd.DataFrame()
|
36
|
+
# Apply the function to each row
|
37
|
+
data[["polarity", "subjectivity"]] = dataset.df[dataset.text_column].apply(
|
38
|
+
lambda x: pd.Series(analyze_sentiment(x))
|
39
|
+
)
|
40
|
+
|
41
|
+
# Create a Plotly scatter plot
|
42
|
+
fig = px.scatter(
|
43
|
+
data, x="polarity", y="subjectivity", title="Polarity vs Subjectivity"
|
44
|
+
)
|
45
|
+
fig.update_traces(textposition="top center")
|
46
|
+
fig.update_layout(xaxis_title="Polarity", yaxis_title="Subjectivity")
|
47
|
+
|
48
|
+
return fig
|
@@ -72,25 +72,24 @@ class Punctuations(Metric):
|
|
72
72
|
text_column = self.inputs.dataset.text_column
|
73
73
|
corpus = create_corpus(self.inputs.dataset.df, text_column=text_column)
|
74
74
|
|
75
|
-
dic = defaultdict(int)
|
76
75
|
special = string.punctuation
|
76
|
+
dic = defaultdict(int, {key: 0 for key in special})
|
77
77
|
for i in corpus:
|
78
78
|
if i in special:
|
79
79
|
dic[i] += 1
|
80
|
-
|
80
|
+
figures = []
|
81
|
+
# if dic:
|
81
82
|
fig = plt.figure()
|
82
83
|
x, y = zip(*dic.items())
|
83
84
|
plt.bar(x, y, color="#17C37B")
|
84
|
-
|
85
|
+
figures.append(
|
86
|
+
Figure(
|
87
|
+
for_object=self,
|
88
|
+
key=self.key,
|
89
|
+
figure=fig,
|
90
|
+
)
|
91
|
+
)
|
85
92
|
# Do this if you want to prevent the figure from being displayed
|
86
93
|
plt.close("all")
|
87
94
|
|
88
|
-
return self.cache_results(
|
89
|
-
figures=[
|
90
|
-
Figure(
|
91
|
-
for_object=self,
|
92
|
-
key=self.key,
|
93
|
-
figure=fig,
|
94
|
-
)
|
95
|
-
]
|
96
|
-
)
|
95
|
+
return self.cache_results(figures=figures)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright © 2023-2024 ValidMind Inc. All rights reserved.
|
2
|
+
# See the LICENSE file in the root of this repository for details.
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
|
4
|
+
|
5
|
+
|
6
|
+
import matplotlib.pyplot as plt
|
7
|
+
import nltk
|
8
|
+
import seaborn as sns
|
9
|
+
from nltk.sentiment import SentimentIntensityAnalyzer
|
10
|
+
|
11
|
+
from validmind import tags, tasks
|
12
|
+
|
13
|
+
|
14
|
+
@tags("data_validation")
|
15
|
+
@tasks("nlp")
|
16
|
+
def Sentiment(dataset):
|
17
|
+
"""
|
18
|
+
Analyzes the sentiment of text data within a dataset using the VADER sentiment analysis tool.
|
19
|
+
|
20
|
+
This method initializes the VADER SentimentIntensityAnalyzer and applies it to each text entry
|
21
|
+
in the specified column of the dataset's dataframe. It returns a KDE plot visualizing the distribution
|
22
|
+
of sentiment scores across the dataset.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
dataset (Dataset): A dataset object which must have a `df` attribute (a pandas DataFrame)
|
26
|
+
and a `text_column` attribute indicating the name of the column containing text.
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
matplotlib.figure.Figure: A KDE plot visualizing the distribution of sentiment scores.
|
30
|
+
"""
|
31
|
+
nltk.download("vader_lexicon", quiet=True)
|
32
|
+
# Initialize VADER
|
33
|
+
sia = SentimentIntensityAnalyzer()
|
34
|
+
|
35
|
+
# Function to get VADER sentiment scores
|
36
|
+
def get_vader_sentiment(text):
|
37
|
+
sentiment_score = sia.polarity_scores(text)
|
38
|
+
return sentiment_score["compound"]
|
39
|
+
|
40
|
+
# Apply the function to each row
|
41
|
+
vader_sentiment = dataset.df[dataset.text_column].apply(get_vader_sentiment)
|
42
|
+
|
43
|
+
fig = plt.figure()
|
44
|
+
ax = sns.kdeplot(
|
45
|
+
x=vader_sentiment,
|
46
|
+
fill=True,
|
47
|
+
common_norm=False,
|
48
|
+
palette="crest",
|
49
|
+
alpha=0.5,
|
50
|
+
linewidth=0,
|
51
|
+
)
|
52
|
+
ax.set_title(f"Sentiment score of {dataset.text_column} ")
|
53
|
+
ax.set_xlabel("Sentiment score")
|
54
|
+
|
55
|
+
plt.close("all")
|
56
|
+
|
57
|
+
return fig
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Copyright © 2023-2024 ValidMind Inc. All rights reserved.
|
2
|
+
# See the LICENSE file in the root of this repository for details.
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
|
4
|
+
|
5
|
+
import evaluate
|
6
|
+
import matplotlib.pyplot as plt
|
7
|
+
import seaborn as sns
|
8
|
+
|
9
|
+
from validmind import tags, tasks
|
10
|
+
|
11
|
+
|
12
|
+
@tags("data_validation")
|
13
|
+
@tasks("nlp")
|
14
|
+
def Toxicity(dataset):
|
15
|
+
"""
|
16
|
+
Analyzes the toxicity of text data within a dataset using a pre-trained toxicity model.
|
17
|
+
|
18
|
+
This method loads a toxicity evaluation model and applies it to each text entry
|
19
|
+
in the specified column of the dataset's dataframe. It returns a KDE plot visualizing the distribution
|
20
|
+
of toxicity scores across the dataset.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
dataset (Dataset): A dataset object which must have a `df` attribute (a pandas DataFrame)
|
24
|
+
and a `text_column` attribute indicating the name of the column containing text.
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
matplotlib.figure.Figure: A KDE plot visualizing the distribution of toxicity scores.
|
28
|
+
"""
|
29
|
+
toxicity = evaluate.load("toxicity")
|
30
|
+
input_text = dataset.df[dataset.text_column]
|
31
|
+
toxicity_scores = toxicity.compute(predictions=list(input_text.values))["toxicity"]
|
32
|
+
|
33
|
+
fig = plt.figure()
|
34
|
+
ax = sns.kdeplot(
|
35
|
+
x=toxicity_scores,
|
36
|
+
fill=True,
|
37
|
+
common_norm=False,
|
38
|
+
palette="crest",
|
39
|
+
alpha=0.5,
|
40
|
+
linewidth=0,
|
41
|
+
)
|
42
|
+
ax.set_title(f"Toxicity score of {dataset.text_column} ")
|
43
|
+
ax.set_xlabel("Toxicity score")
|
44
|
+
plt.close("all")
|
45
|
+
return fig
|
validmind/tests/decorator.py
CHANGED
@@ -15,6 +15,7 @@ import pandas as pd
|
|
15
15
|
|
16
16
|
from validmind.errors import MissingRequiredTestInputError
|
17
17
|
from validmind.logging import get_logger
|
18
|
+
from validmind.utils import get_description_metadata
|
18
19
|
from validmind.vm_models import (
|
19
20
|
Metric,
|
20
21
|
MetricResult,
|
@@ -113,20 +114,24 @@ def _build_result(results, test_id, description, output_template, inputs): # no
|
|
113
114
|
else:
|
114
115
|
process_item(results)
|
115
116
|
|
117
|
+
result_summary = ResultSummary(results=tables)
|
118
|
+
|
116
119
|
return MetricResultWrapper(
|
117
120
|
result_id=test_id,
|
118
121
|
metric=MetricResult(
|
119
122
|
key=test_id,
|
120
123
|
ref_id=ref_id,
|
121
124
|
value="Empty",
|
122
|
-
summary=
|
125
|
+
summary=result_summary,
|
123
126
|
),
|
124
127
|
figures=figures,
|
125
128
|
result_metadata=[
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
129
|
+
get_description_metadata(
|
130
|
+
test_id=test_id,
|
131
|
+
default_description=description,
|
132
|
+
summary=result_summary.serialize(),
|
133
|
+
figures=figures,
|
134
|
+
)
|
130
135
|
],
|
131
136
|
inputs=inputs,
|
132
137
|
output_template=output_template,
|
@@ -153,7 +158,7 @@ def _get_run_method(func, inputs, params):
|
|
153
158
|
test_id=self.test_id,
|
154
159
|
description=inspect.getdoc(self),
|
155
160
|
output_template=self.output_template,
|
156
|
-
inputs=
|
161
|
+
inputs=self.get_accessed_inputs(),
|
157
162
|
)
|
158
163
|
|
159
164
|
return self.result
|
@@ -264,7 +269,7 @@ def metric(func_or_id):
|
|
264
269
|
{
|
265
270
|
"run": _get_run_method(func, inputs, params),
|
266
271
|
"required_inputs": list(inputs.keys()),
|
267
|
-
"
|
272
|
+
"default_params": {k: v["default"] for k, v in params.items()},
|
268
273
|
"__doc__": description,
|
269
274
|
"metadata": {
|
270
275
|
"task_types": tasks,
|