DaSPi 2.1.0__tar.gz → 2.2.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.
- {daspi-2.1.0 → daspi-2.2.0}/PKG-INFO +1 -1
- {daspi-2.1.0 → daspi-2.2.0}/daspi/anova/model.py +113 -33
- {daspi-2.1.0 → daspi-2.2.0}/daspi/anova/tables.py +16 -2
- {daspi-2.1.0 → daspi-2.2.0}/daspi/constants.py +1 -1
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/plotter.py +27 -13
- {daspi-2.1.0 → daspi-2.2.0}/pyproject.toml +1 -1
- {daspi-2.1.0 → daspi-2.2.0}/tests/anova_model_test.py +22 -2
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_alignments_basic.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_all_alignments.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_angles_basic.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_callable_formatters.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_combined_all_features.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_extreme_angles.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_formatters_basic.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_formatters_scientific.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_multivariate_formatting.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_orientation_mapping.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_percentage_formatter.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_comparison_with_violin.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_custom_color.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_custom_width.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_joint_chart_mixed_plots.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_multivariate_chart_facets.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_resolution_high.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_horizontal.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_no_fade.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_no_fill.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_with_hue.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/JointChart_bar_stacking.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/JointChart_pareto_marked.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/JointChart_probability_dists-prob.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/JointChart_regression_kde.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/MultivariateChart_full-stripes.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/MultivariateChart_full.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_bivariate-univariate-charts_contour-mean-test.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_bivariate-univariate-charts_mean-test.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_bivariate-univariate-charts_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_bivariate-univariate-charts_smoothed.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_bivariate-univariate-charts_user.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_pair_comparison_charts_basic.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_parameter_relevance_charts_basic.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_process-capabity-analysis-charts_hue.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_residual_charts_basic.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_KDE_multiple.png +0 -0
- daspi-2.2.0/tests/charts/SingleChart_KDE_ridge.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_beeswarm_multiple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_beeswarm_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_blandaltman_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_blandaltman_user.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_box_fill.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_box_multiple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_box_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_categorical-float-features_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_errorbar_mean-test.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_errorbar_sem.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_errorbar_std-test.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_errorbar_var-test.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_jitter_multiple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_jitter_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_line_hue.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_line_hue_size.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_pareto_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_pareto_transposed.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_quantiles_multiple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_quantiles_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_full.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_hue-shape.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_hue-size.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_hue.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_shape.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_size-shape.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_size.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_violin_mono.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_violin_multiple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_violin_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/plotlib_visual_test.py +32 -0
- {daspi-2.1.0 → daspi-2.2.0}/LICENSE +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/README.md +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/__init__.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/_typing.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/_version.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/anova/__init__.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/anova/convert.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/config.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/__init__.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/ancova.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/anova.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/anova2.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/anova2_unbalanced.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/anova3.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/anova3_unbalanced.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/carpet_full-factorial.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/covariate.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/drop_card.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/eeprom_full-factorial.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/eeprom_partial-factorial.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/grnr_adjustment.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/grnr_layer_thickness.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/grnr_spc.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/iris.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/mpg.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/painkillers-dissolution.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/partial_factorial.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/penguins.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/process_capability.mpx +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/salt_sticks.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/shoe-sole.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/datasets/tips.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/doe/__init__.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/doe/build.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/__init__.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/appearance.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/chart.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/classify.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/facets.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/precast.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/styles/daspi-dark.mplstyle +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/styles/daspi.mplstyle +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/styles/ggplot2.mplstyle +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/plotlib/styles/seaborn.mplstyle +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/requirements.txt +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/statistics/__init__.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/statistics/confidence.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/statistics/estimation.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/statistics/hypothesis.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/statistics/montecarlo.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/daspi/strings.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/ANOVA_SS_Fehler.mpx +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/ANOVA_SS_Fehler.mpx.bak +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/GageRnR.mpx +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/GageRnR.mpx.bak +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/__init__.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/anova_convert_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/anova_tables_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/JointChart_kdes_kde-mean.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/JointChart_kdes_mixed-kde.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/JointChart_probability_norm-prob.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_gage_rnr_model_basic.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_gage_study_model_basic.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_KDE_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_simple-stripes-65ci.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_simple-stripes-99ci.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_simple-stripes.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_scatter_transposed.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_violine-contour_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_violine_mono.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_violine_multiple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/charts/SingleChart_violine_simple.png +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/config_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/data/anova3_result.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/data/dists_10-samples.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/data/dists_25-samples.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/data/gage_rnr.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/data/gage_study.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/data/generate_dists.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/data/grnr_d-optimal.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/data/uncertainties.csv +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/doe_build_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/plotlib_appearance_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/plotlib_chart_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/plotlib_classify_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/plotlib_facets_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/plotlib_plotter_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/plotlib_templates_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/r2_pred.mpx +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/r2_pred.mpx.bak +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/statistics_confidence_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/statistics_estimation_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/statistics_hypothesis_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/statistics_montecarlo_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/strings_test.py +0 -0
- {daspi-2.1.0 → daspi-2.2.0}/tests/test_full_factorial_design_builder.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: DaSPi
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Visualize and analyze your data with DaSPi. This package is designed for users who want to find relevant influencing factors in processes and validate improvements.
|
|
5
5
|
Author-Email: =?utf-8?q?Reto_J=C3=A4ggli?= <reto@jaeggli.email>
|
|
6
6
|
License: MIT
|
|
@@ -76,7 +76,8 @@ import itertools
|
|
|
76
76
|
import warnings
|
|
77
77
|
from abc import ABC, abstractmethod
|
|
78
78
|
from collections.abc import Generator
|
|
79
|
-
from
|
|
79
|
+
from functools import lru_cache
|
|
80
|
+
from typing import Any, Literal, LiteralString, Self, overload
|
|
80
81
|
|
|
81
82
|
import numpy as np
|
|
82
83
|
import pandas as pd
|
|
@@ -110,19 +111,61 @@ __all__ = [
|
|
|
110
111
|
'GageStudyModel',
|
|
111
112
|
'GeneralizedLinearModel',
|
|
112
113
|
'LinearModel',
|
|
113
|
-
'get_order',
|
|
114
114
|
'hierarchical',
|
|
115
|
-
'
|
|
115
|
+
'interaction_order',
|
|
116
|
+
'is_main_parameter',
|
|
117
|
+
]
|
|
116
118
|
|
|
117
119
|
|
|
120
|
+
@overload
|
|
121
|
+
def _drop_intercept(data: DataFrame) -> DataFrame: ...
|
|
122
|
+
@overload
|
|
123
|
+
def _drop_intercept(data: Series) -> Series: ...
|
|
124
|
+
def _drop_intercept(data: DataFrame | Series) -> DataFrame | Series:
|
|
125
|
+
"""Drop the intercept index from a DataFrame or Series if it exists.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
data : DataFrame or Series
|
|
130
|
+
The input data from which to drop the intercept.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
DataFrame or Series
|
|
135
|
+
The input data with the intercept dropped, if it was present.
|
|
136
|
+
"""
|
|
137
|
+
mask = ~data.index.str.contains(ANOVA.INTERCEPT, regex=False)
|
|
138
|
+
if isinstance(data, Series):
|
|
139
|
+
data = data[mask]
|
|
140
|
+
else:
|
|
141
|
+
data = data.loc[mask, :]
|
|
142
|
+
return data.copy()
|
|
143
|
+
|
|
118
144
|
def is_main_parameter(parameter: str) -> bool:
|
|
119
145
|
"""Check if given parameter is a main parameter (intercept is
|
|
120
146
|
excluded)."""
|
|
121
147
|
return parameter != ANOVA.INTERCEPT and ANOVA.SEP not in parameter
|
|
122
148
|
|
|
123
|
-
def
|
|
149
|
+
def interaction_order(parameter: str) -> int:
|
|
124
150
|
"""Get the order of a parameter (i.e., how many interactions it
|
|
125
|
-
contains).
|
|
151
|
+
contains).
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
parameter : str
|
|
156
|
+
The parameter name to check.
|
|
157
|
+
|
|
158
|
+
Returns
|
|
159
|
+
-------
|
|
160
|
+
int
|
|
161
|
+
The interaction order of the parameter.
|
|
162
|
+
|
|
163
|
+
Examples
|
|
164
|
+
--------
|
|
165
|
+
```python
|
|
166
|
+
interaction_order('x1') # Returns 1
|
|
167
|
+
interaction_order('x1:x2') # Returns 2
|
|
168
|
+
```"""
|
|
126
169
|
return parameter.count(ANOVA.SEP) + 1
|
|
127
170
|
|
|
128
171
|
def hierarchical(parameters: list[str]) -> list[str]:
|
|
@@ -139,21 +182,28 @@ def hierarchical(parameters: list[str]) -> list[str]:
|
|
|
139
182
|
-------
|
|
140
183
|
h_parameters : list of str
|
|
141
184
|
Sorted factors for hierarchical model"""
|
|
142
|
-
|
|
185
|
+
return list(_hierarchical_cached(tuple(parameters)))
|
|
186
|
+
|
|
187
|
+
@lru_cache(maxsize=128)
|
|
188
|
+
def _hierarchical_cached(parameters: tuple[str, ...]) -> tuple[str, ...]:
|
|
189
|
+
"""Cached implementation of `hierarchical`. Repeated calls with the
|
|
190
|
+
same (unchanged) set of terms, e.g. from `is_hierarchical` during a
|
|
191
|
+
single fit, are then resolved without recomputing all interaction
|
|
192
|
+
subsets."""
|
|
143
193
|
h_parameters = set(parameters)
|
|
144
194
|
for parameter in parameters:
|
|
145
195
|
split = parameter.split(ANOVA.SEP)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if n_splits <= ANOVA.SMALLEST_INTERACTION:
|
|
196
|
+
_order = len(split)
|
|
197
|
+
h_parameters.update(split)
|
|
198
|
+
if _order <= ANOVA.SMALLEST_INTERACTION_ORDER:
|
|
150
199
|
continue
|
|
151
200
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
201
|
+
h_parameters.update(
|
|
202
|
+
ANOVA.SEP.join(combo)
|
|
203
|
+
for i in range(ANOVA.SMALLEST_INTERACTION_ORDER, _order)
|
|
204
|
+
for combo in itertools.combinations(split, i))
|
|
155
205
|
|
|
156
|
-
return
|
|
206
|
+
return tuple(sorted(h_parameters, key=lambda p: (interaction_order(p), p)))
|
|
157
207
|
|
|
158
208
|
|
|
159
209
|
class BaseHTMLReprModel(ABC):
|
|
@@ -671,21 +721,55 @@ class LinearModel(BaseHTMLReprModel):
|
|
|
671
721
|
"""Check if current fitted model is hierarchical."""
|
|
672
722
|
return all(term in self.terms for term in hierarchical(self.terms))
|
|
673
723
|
|
|
674
|
-
def effects(self) -> Series:
|
|
724
|
+
def effects(self, include_intercept: bool = True) -> Series:
|
|
675
725
|
"""Calculates the impact of each term on the target. The
|
|
676
726
|
effects are described as absolute number of the parameter
|
|
677
|
-
coefficients devided by its standard error.
|
|
727
|
+
coefficients devided by its standard error.
|
|
728
|
+
|
|
729
|
+
Parameters
|
|
730
|
+
----------
|
|
731
|
+
include_intercept : bool, optional
|
|
732
|
+
If False, the intercept is dropped from the returned
|
|
733
|
+
Series, by default True.
|
|
734
|
+
|
|
735
|
+
Returns
|
|
736
|
+
-------
|
|
737
|
+
Series
|
|
738
|
+
A Pandas Series containing the effects of each term on the
|
|
739
|
+
target variable. The index of the Series corresponds to the
|
|
740
|
+
term names, and the values represent the calculated effects.
|
|
741
|
+
"""
|
|
678
742
|
if self._effects.empty:
|
|
679
743
|
self._effects = terms_effect(self.model)
|
|
680
744
|
effects = self._effects.copy().rename(index=self.term_map)
|
|
745
|
+
if not include_intercept:
|
|
746
|
+
effects = _drop_intercept(effects)
|
|
681
747
|
return effects
|
|
682
748
|
|
|
683
|
-
def p_values(self) -> 'Series[float]':
|
|
749
|
+
def p_values(self, include_intercept: bool = True) -> 'Series[float]':
|
|
684
750
|
"""Get P-value for significance of adding model terms using
|
|
685
|
-
anova typ III table for current model.
|
|
751
|
+
anova typ III table for current model.
|
|
752
|
+
|
|
753
|
+
Parameters
|
|
754
|
+
----------
|
|
755
|
+
include_intercept : bool, optional
|
|
756
|
+
If False, the intercept is dropped from the returned
|
|
757
|
+
Series, by default True.
|
|
758
|
+
|
|
759
|
+
Returns
|
|
760
|
+
-------
|
|
761
|
+
Series
|
|
762
|
+
A Pandas Series containing the p-values of each term on the
|
|
763
|
+
target variable. The index of the Series corresponds to the
|
|
764
|
+
term names, and the values represent the calculated
|
|
765
|
+
p-values.
|
|
766
|
+
"""
|
|
686
767
|
if self._p_values.empty:
|
|
687
768
|
self._p_values = terms_probability(self.model)
|
|
688
|
-
|
|
769
|
+
p_values = self._p_values.copy().rename(index=self.term_map)
|
|
770
|
+
if not include_intercept:
|
|
771
|
+
p_values = _drop_intercept(p_values)
|
|
772
|
+
return p_values
|
|
689
773
|
|
|
690
774
|
def least_parameter(self) -> str:
|
|
691
775
|
"""Get the parameter name with the least effect or the least
|
|
@@ -706,20 +790,15 @@ class LinearModel(BaseHTMLReprModel):
|
|
|
706
790
|
the term name with the least p-value for the F-stats coming from
|
|
707
791
|
current ANOVA table.
|
|
708
792
|
"""
|
|
709
|
-
|
|
710
|
-
|
|
793
|
+
include_intercept = not self.skip_intercept_as_least
|
|
794
|
+
p_values = self.p_values(include_intercept)
|
|
711
795
|
if any(p_values.isna()):
|
|
712
|
-
effects = self.effects().copy()
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
else:
|
|
718
|
-
leasts = effects[effects == effects.min()]
|
|
719
|
-
least = max(leasts.index, key=get_order)
|
|
796
|
+
effects = self.effects(include_intercept).copy()
|
|
797
|
+
effect_mask = effects.isna()
|
|
798
|
+
if not any(effect_mask):
|
|
799
|
+
effect_mask = effects == effects.min()
|
|
800
|
+
least = max(effects[effect_mask].index, key=interaction_order )
|
|
720
801
|
else:
|
|
721
|
-
if has_intercept and self.skip_intercept_as_least:
|
|
722
|
-
p_values = p_values.drop(ANOVA.INTERCEPT)
|
|
723
802
|
least = p_values.iloc[::-1].idxmax()
|
|
724
803
|
return str(least)
|
|
725
804
|
|
|
@@ -945,7 +1024,8 @@ class LinearModel(BaseHTMLReprModel):
|
|
|
945
1024
|
get_term_name(self.feature_map.get(x, x))
|
|
946
1025
|
for x in parameter.split(ANOVA.SEP))
|
|
947
1026
|
assert term in self.terms, (
|
|
948
|
-
f'Given term {term} is not in model'
|
|
1027
|
+
f'Given term {term} (or "{parameter}") is not in model, '
|
|
1028
|
+
f'got: {self.terms}')
|
|
949
1029
|
|
|
950
1030
|
self.excluded.add(term)
|
|
951
1031
|
return self
|
|
@@ -985,7 +1065,7 @@ class LinearModel(BaseHTMLReprModel):
|
|
|
985
1065
|
UserWarning)
|
|
986
1066
|
self.excluded.discard(term)
|
|
987
1067
|
return self
|
|
988
|
-
|
|
1068
|
+
|
|
989
1069
|
def recursive_elimination(
|
|
990
1070
|
self,
|
|
991
1071
|
rsquared_max: float = 0.99,
|
|
@@ -97,9 +97,23 @@ def terms_effect(model: RegressionResultsWrapper) -> Series:
|
|
|
97
97
|
Series
|
|
98
98
|
A pandas Series containing the effects of each factor
|
|
99
99
|
on the target variable.
|
|
100
|
+
|
|
101
|
+
Notes
|
|
102
|
+
-----
|
|
103
|
+
The effect size is calculated as the absolute value of the parameter
|
|
104
|
+
coefficient divided by its standard error. This is a measure of the
|
|
105
|
+
strength of the relationship between the predictor and the target
|
|
106
|
+
variable, with larger values indicating a stronger effect.
|
|
107
|
+
|
|
108
|
+
The index of the returned Series corresponds to the factor names in
|
|
109
|
+
the model, and the values represent the calculated effect sizes.
|
|
110
|
+
|
|
111
|
+
Only terms that are present in the model are included in the output,
|
|
112
|
+
which is determined by filtering the effects based on the unique
|
|
113
|
+
factor names derived from the model's design information.
|
|
100
114
|
"""
|
|
101
115
|
params: Series = model.params
|
|
102
|
-
|
|
116
|
+
terms = model.model.data.design_info.term_names
|
|
103
117
|
names_map = {n: get_term_name(n) for n in params.index}
|
|
104
118
|
se = (model.bse
|
|
105
119
|
.rename(index=names_map)
|
|
@@ -112,7 +126,7 @@ def terms_effect(model: RegressionResultsWrapper) -> Series:
|
|
|
112
126
|
.groupby(level=0)
|
|
113
127
|
.sum())
|
|
114
128
|
effects = params.abs() / se
|
|
115
|
-
effects = effects[uniques(names_map.values())]
|
|
129
|
+
effects = effects[uniques(n for n in names_map.values() if n in terms)] # Filter out terms that are not in the model (this can happen when the model is imbalanced and some terms are dropped)
|
|
116
130
|
effects.name = ANOVA.EFFECTS
|
|
117
131
|
effects.index.name = ANOVA.FACTORS
|
|
118
132
|
return effects
|
|
@@ -549,7 +549,7 @@ class _Anova_:
|
|
|
549
549
|
""""Default name for source used in ANOVA table."""
|
|
550
550
|
VIF: Literal['VIF'] = 'VIF'
|
|
551
551
|
""""Default column name for VIF values in ANOVA table."""
|
|
552
|
-
|
|
552
|
+
SMALLEST_INTERACTION_ORDER: int = 2
|
|
553
553
|
"""Smallest possible interaction"""
|
|
554
554
|
RESIDUAL: Literal['Residual'] = 'Residual'
|
|
555
555
|
"""Name in anova table for residual (not explained) values."""
|
|
@@ -2088,7 +2088,7 @@ class TransformPlotter(Plotter):
|
|
|
2088
2088
|
self.skip_na = skip_na
|
|
2089
2089
|
|
|
2090
2090
|
df = pd.concat(
|
|
2091
|
-
|
|
2091
|
+
(self.transform(f, t) for f, t in self.feature_grouped(source)),
|
|
2092
2092
|
axis=0,
|
|
2093
2093
|
ignore_index=True)
|
|
2094
2094
|
|
|
@@ -2120,24 +2120,29 @@ class TransformPlotter(Plotter):
|
|
|
2120
2120
|
target_data : pandas Series
|
|
2121
2121
|
feature grouped target data used for transformation.
|
|
2122
2122
|
"""
|
|
2123
|
+
feature_data = self._f_base
|
|
2123
2124
|
if self.feature and self.feature != PLOTTER.TRANSFORMED_FEATURE:
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2125
|
+
use_feature_value = any((
|
|
2126
|
+
pd.api.types.is_numeric_dtype(source[self.feature]),
|
|
2127
|
+
pd.api.types.is_datetime64_dtype(source[self.feature]),
|
|
2128
|
+
pd.api.types.is_timedelta64_dtype(source[self.feature])))
|
|
2129
|
+
|
|
2130
|
+
grouped = source.groupby(self.feature, sort=True, observed=True)
|
|
2131
|
+
for feature_value, group in grouped:
|
|
2127
2132
|
target_data = group[self.target]
|
|
2128
2133
|
if self.skip_na and getattr(target_data.isna(), self.skip_na)():
|
|
2129
2134
|
continue
|
|
2130
2135
|
|
|
2131
|
-
self._original_f_values
|
|
2132
|
-
if
|
|
2133
|
-
|
|
2136
|
+
self._original_f_values += (feature_value,)
|
|
2137
|
+
if use_feature_value:
|
|
2138
|
+
yield feature_value, target_data
|
|
2134
2139
|
else:
|
|
2135
|
-
feature_data
|
|
2136
|
-
|
|
2140
|
+
yield feature_data, target_data
|
|
2141
|
+
feature_data += 1
|
|
2142
|
+
|
|
2137
2143
|
else:
|
|
2138
2144
|
self.feature = PLOTTER.TRANSFORMED_FEATURE
|
|
2139
2145
|
self._original_f_values = (self._f_base, )
|
|
2140
|
-
feature_data = self._f_base
|
|
2141
2146
|
target_data = source[self.target]
|
|
2142
2147
|
yield feature_data, target_data
|
|
2143
2148
|
|
|
@@ -4294,6 +4299,13 @@ class GaussianKDE(SpreadOpacity, TransformPlotter):
|
|
|
4294
4299
|
plot.
|
|
4295
4300
|
target : str
|
|
4296
4301
|
Column name of the target variable for the plot.
|
|
4302
|
+
feature : str, optional
|
|
4303
|
+
Column name of the feature variable for the plot, by default ''.
|
|
4304
|
+
Please note that this class is designed for univariate plots, so
|
|
4305
|
+
by default, all curves will be drawn on the same feature axis.
|
|
4306
|
+
If you want to draw curves on different feature axes, change the
|
|
4307
|
+
"ignore_feature" parameter to False and provide a feature column
|
|
4308
|
+
in the source DataFrame.
|
|
4297
4309
|
stretch : float, optional
|
|
4298
4310
|
Factor by which the curve was stretched in height,
|
|
4299
4311
|
by default 1.
|
|
@@ -4446,10 +4458,11 @@ class GaussianKDE(SpreadOpacity, TransformPlotter):
|
|
|
4446
4458
|
self,
|
|
4447
4459
|
source: DataFrame,
|
|
4448
4460
|
target: str,
|
|
4461
|
+
feature: str = '',
|
|
4449
4462
|
stretch: float = 1,
|
|
4450
4463
|
height: float | None = None,
|
|
4451
4464
|
skip_na: Literal['all', 'any'] | None = None,
|
|
4452
|
-
ignore_feature: bool = True,
|
|
4465
|
+
ignore_feature: bool = True, # TODO: This option seems not to work at all: fix it
|
|
4453
4466
|
margin: float = 0,
|
|
4454
4467
|
fill: bool = True,
|
|
4455
4468
|
agreements: tuple[float, ...] | tuple[int, ...] = DEFAULT.AGREEMENTS,
|
|
@@ -4467,15 +4480,16 @@ class GaussianKDE(SpreadOpacity, TransformPlotter):
|
|
|
4467
4480
|
self.n_points = n_points
|
|
4468
4481
|
self.margin = margin
|
|
4469
4482
|
self.fill = fill
|
|
4470
|
-
if
|
|
4483
|
+
if feature and not ignore_feature:
|
|
4471
4484
|
height = height if height else CATEGORY.FEATURE_SPACE
|
|
4472
4485
|
else:
|
|
4473
|
-
|
|
4486
|
+
feature = PLOTTER.TRANSFORMED_FEATURE
|
|
4474
4487
|
self._height = height
|
|
4475
4488
|
f_base = kwds.pop('f_base', DEFAULT.FEATURE_BASE)
|
|
4476
4489
|
super().__init__(
|
|
4477
4490
|
source=source,
|
|
4478
4491
|
target=target,
|
|
4492
|
+
feature=feature,
|
|
4479
4493
|
f_base=f_base,
|
|
4480
4494
|
skip_na=skip_na,
|
|
4481
4495
|
target_on_y=target_on_y,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "DaSPi"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.2.0"
|
|
4
4
|
description = "Visualize and analyze your data with DaSPi. This package is designed for users who want to find relevant influencing factors in processes and validate improvements."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Reto Jäggli", email = "reto@jaeggli.email" },
|
|
@@ -216,6 +216,26 @@ class TestLinearModel:
|
|
|
216
216
|
assert p_values.max() > 0.05
|
|
217
217
|
assert lm.least_parameter() == 'A'
|
|
218
218
|
|
|
219
|
+
def test_effects_include_intercept(self, lm: LinearModel) -> None:
|
|
220
|
+
lm.fit()
|
|
221
|
+
effects = lm.effects()
|
|
222
|
+
assert 'Intercept' in effects.index
|
|
223
|
+
effects_no_intercept = lm.effects(include_intercept=False)
|
|
224
|
+
assert 'Intercept' not in effects_no_intercept.index
|
|
225
|
+
assert len(effects_no_intercept) == len(effects) - 1
|
|
226
|
+
pd.testing.assert_series_equal(
|
|
227
|
+
effects_no_intercept, effects.drop('Intercept'))
|
|
228
|
+
|
|
229
|
+
def test_p_values_include_intercept(self, lm: LinearModel) -> None:
|
|
230
|
+
lm.fit()
|
|
231
|
+
p_values = lm.p_values()
|
|
232
|
+
assert 'Intercept' in p_values.index
|
|
233
|
+
p_values_no_intercept = lm.p_values(include_intercept=False)
|
|
234
|
+
assert 'Intercept' not in p_values_no_intercept.index
|
|
235
|
+
assert len(p_values_no_intercept) == len(p_values) - 1
|
|
236
|
+
pd.testing.assert_series_equal(
|
|
237
|
+
p_values_no_intercept, p_values.drop('Intercept'))
|
|
238
|
+
|
|
219
239
|
def test_main_parameters_property(self, lm2: LinearModel) -> None:
|
|
220
240
|
lm2.fit()
|
|
221
241
|
assert lm2.main_parameters == ['A', 'B', 'C', 'bad']
|
|
@@ -367,10 +387,10 @@ class TestLinearModel:
|
|
|
367
387
|
|
|
368
388
|
def test_eliminate_invalid_term(self, lm3: LinearModel) -> None:
|
|
369
389
|
lm3.fit()
|
|
370
|
-
with pytest.raises(AssertionError, match=r'Given term Q is not in model'):
|
|
390
|
+
with pytest.raises(AssertionError, match=r'Given term Q .* is not in model'):
|
|
371
391
|
lm3.eliminate('Q')
|
|
372
392
|
|
|
373
|
-
with pytest.raises(AssertionError, match=r'Given term x0:x1:x2 is not in model'):
|
|
393
|
+
with pytest.raises(AssertionError, match=r'Given term x0:x1:x2 .* is not in model'):
|
|
374
394
|
lm3.eliminate('A:B:C')
|
|
375
395
|
|
|
376
396
|
def test_eliminate_encoded_term(self, lm3: LinearModel) -> None:
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/Formatting_Features_Test_multivariate_formatting.png
RENAMED
|
Binary file
|
|
Binary file
|
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_comparison_with_violin.png
RENAMED
|
Binary file
|
|
Binary file
|
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_joint_chart_mixed_plots.png
RENAMED
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_multivariate_chart_facets.png
RENAMED
|
Binary file
|
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_horizontal.png
RENAMED
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_no_fade.png
RENAMED
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_no_fill.png
RENAMED
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_simple.png
RENAMED
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/GaussianKDEContourUnivariate_single_chart_with_hue.png
RENAMED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
{daspi-2.1.0 → daspi-2.2.0}/tests/charts/Precasts_bivariate-univariate-charts_contour-mean-test.png
RENAMED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -677,6 +677,38 @@ class TestSingleChart:
|
|
|
677
677
|
assert STR.today in info_msg
|
|
678
678
|
assert STR.username in info_msg
|
|
679
679
|
assert self.info_msg not in info_msg
|
|
680
|
+
|
|
681
|
+
self.kind = 'ridge'
|
|
682
|
+
chart = SingleChart(
|
|
683
|
+
source=df_painkillers,
|
|
684
|
+
target=self.target,
|
|
685
|
+
feature=self.cat1,
|
|
686
|
+
target_on_y=True
|
|
687
|
+
).plot(
|
|
688
|
+
GaussianKDE,
|
|
689
|
+
visible_spines='target',
|
|
690
|
+
hide_axis='feature',
|
|
691
|
+
agreements=(),
|
|
692
|
+
ignore_feature=False,
|
|
693
|
+
).label(
|
|
694
|
+
fig_title=self.fig_title,
|
|
695
|
+
sub_title=self.sub_title,
|
|
696
|
+
feature_label=True,
|
|
697
|
+
target_label=True,
|
|
698
|
+
info=True
|
|
699
|
+
).save(self.file_name
|
|
700
|
+
).close()
|
|
701
|
+
texts = get_texts(chart)
|
|
702
|
+
info_msg = texts[-1].get_text()
|
|
703
|
+
assert self.file_name.is_file()
|
|
704
|
+
assert len(texts) == 5
|
|
705
|
+
assert texts[0].get_text() == self.fig_title
|
|
706
|
+
assert texts[1].get_text() == self.sub_title
|
|
707
|
+
assert texts[2].get_text() == self.target
|
|
708
|
+
assert texts[3].get_text() == self.cat1
|
|
709
|
+
assert STR.today in info_msg
|
|
710
|
+
assert STR.username in info_msg
|
|
711
|
+
assert self.info_msg not in info_msg
|
|
680
712
|
|
|
681
713
|
def test_beeswarm_plot(self) -> None:
|
|
682
714
|
self.base = f'{self.fig_title}_beeswarm'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|