Skip to content

Commit fefa960

Browse files
committed
final commiyts before version bump, added correlate, skew-report, boostrap that returns the confidence interval, permutation tests and effect size, major statistics for data analysis.
1 parent 0126571 commit fefa960

8 files changed

Lines changed: 728 additions & 0 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ One import, one line. A clean, sorted DataFrame you can read or feed into the ne
8282
| `pca_variance` | How much variance does each principal component explain? |
8383
| `pca_loadings` | What does each principal component consist of? |
8484
| `imbalance` | How skewed are the classes in a target column? |
85+
| `correlate` | Which features move together, and is it significant? |
86+
| `skew_report` | How skewed is each column, and what transform helps? |
87+
| `bootstrap_ci` | What is the confidence interval for a statistic? |
88+
| `permutation_test` | Are two groups really different? (a p-value) |
89+
| `effect_size` | How big is the difference, not just whether it is significant? |
8590
| `difference` | How far apart are two values or columns? |
8691
| `split` | How does a total divide across weights or groups? |
8792
| `display` | Format numbers or a column as clean "%" strings |

docs/documentation.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,204 @@ Pass a single column (`df["target"]`), not the whole DataFrame. Nulls are droppe
496496

497497
---
498498

499+
## `correlate`
500+
501+
Correlation with p-values, the piece `df.corr()` leaves out. Pass two Series for a single `(r, p)`, or a whole DataFrame for a tidy table of every numeric pair, strongest first.
502+
503+
!!! tip "Similar concept"
504+
`scipy.stats.pearsonr` / `scipy.stats.spearmanr`
505+
506+
**Signature**
507+
508+
```python
509+
correlate(a, b=None, method="pearson", decimals=2)
510+
```
511+
512+
**Two columns return `(r, p)`**
513+
514+
```python
515+
import numpy as np, pandas as pd
516+
from percentify import correlate
517+
518+
np.random.seed(7)
519+
base = np.random.randn(200)
520+
df = pd.DataFrame({
521+
"age": base,
522+
"income": base * 2 + np.random.randn(200) * 0.3, # tracks age
523+
"score": np.random.randn(200),
524+
})
525+
526+
correlate(df["age"], df["income"]) # (0.99, 0.0)
527+
```
528+
529+
**A DataFrame returns a ranked table**
530+
531+
```python
532+
correlate(df)
533+
```
534+
535+
```text
536+
feature_1 feature_2 r p
537+
age income 0.99 0.00
538+
age score 0.05 0.49
539+
income score 0.05 0.49
540+
```
541+
542+
Pass `method="spearman"` for rank (monotonic) correlation.
543+
544+
---
545+
546+
## `skew_report`
547+
548+
Distribution shape for every numeric column: skew, kurtosis, outlier percentage, and a suggested transform. Most-skewed first.
549+
550+
!!! tip "Similar concept"
551+
`pandas.DataFrame.skew` + `pandas.DataFrame.kurt`
552+
553+
**Signature**
554+
555+
```python
556+
skew_report(df, decimals=2)
557+
```
558+
559+
**Example**
560+
561+
```python
562+
import numpy as np, pandas as pd
563+
from percentify import skew_report
564+
565+
np.random.seed(1)
566+
df = pd.DataFrame({
567+
"income": np.random.exponential(2, 500), # right-skewed
568+
"age": np.random.normal(40, 10, 500), # roughly symmetric
569+
})
570+
571+
skew_report(df)
572+
```
573+
574+
```text
575+
feature skew kurtosis outlier_pct suggested_transform
576+
income 1.55 2.77 3.8 log1p
577+
age -0.01 0.24 0.8 none
578+
```
579+
580+
`suggested_transform` is a starting point: `log1p` for right-skewed non-negative data, `yeo-johnson` for skew with negatives, and `none` when a column is already roughly symmetric. The `outlier_pct` column reuses [`outliers`](#outliers).
581+
582+
---
583+
584+
## `bootstrap_ci`
585+
586+
A confidence interval for any statistic, with no distribution assumed. It resamples the data with replacement and reads the percentiles of the resampled statistic.
587+
588+
!!! tip "Similar concept"
589+
`scipy.stats.bootstrap`
590+
591+
**Signature**
592+
593+
```python
594+
bootstrap_ci(data, statistic=np.mean, ci=95, n_resamples=1000, decimals=2, random_state=None)
595+
```
596+
597+
**Example**
598+
599+
```python
600+
import numpy as np
601+
from percentify import bootstrap_ci
602+
603+
np.random.seed(1)
604+
income = np.random.exponential(2, 500)
605+
606+
bootstrap_ci(income, random_state=0) # (1.88, 2.22)
607+
```
608+
609+
Pass any `statistic` (for example `np.median`), change the level with `ci`, and set `random_state` for a reproducible interval.
610+
611+
---
612+
613+
## `permutation_test`
614+
615+
A distribution-free p-value for the difference between two samples. It shuffles the group labels many times and asks how often chance alone produces a difference as large as the one you saw.
616+
617+
!!! tip "Similar concept"
618+
`scipy.stats.permutation_test`
619+
620+
**Signature**
621+
622+
```python
623+
permutation_test(a, b, statistic=None, n_permutations=1000, decimals=4, random_state=None)
624+
```
625+
626+
**Example**
627+
628+
```python
629+
import numpy as np
630+
from percentify import permutation_test
631+
632+
np.random.seed(1)
633+
a = np.random.normal(100, 15, 120)
634+
b = np.random.normal(106, 15, 120)
635+
636+
permutation_test(a, b, random_state=0) # 0.001
637+
```
638+
639+
The default statistic is the difference in means; pass your own `statistic(a, b)` for anything else. It returns the number, not a pass or fail verdict, so the judgement stays with you.
640+
641+
---
642+
643+
## `effect_size`
644+
645+
How big is the difference, not just whether it is significant. It detects the outcome type and reports the right measure.
646+
647+
!!! tip "Similar concept"
648+
`pingouin.compute_effsize`
649+
650+
**Signature**
651+
652+
```python
653+
effect_size(df, group, value, decimals=2)
654+
```
655+
656+
**Numeric outcome: Cohen's d, Hedges' g, mean difference**
657+
658+
```python
659+
import numpy as np, pandas as pd
660+
from percentify import effect_size
661+
662+
np.random.seed(1)
663+
df = pd.DataFrame({
664+
"variant": ["A"] * 500 + ["B"] * 500,
665+
"revenue": np.concatenate([np.random.normal(100, 20, 500),
666+
np.random.normal(112, 20, 500)]),
667+
})
668+
669+
effect_size(df, group="variant", value="revenue")
670+
```
671+
672+
```text
673+
comparison cohen_d hedges_g mean_diff interpretation
674+
A vs B -0.58 -0.58 -11.42 medium
675+
```
676+
677+
**Binary outcome (two levels): Cohen's h and lift**
678+
679+
```python
680+
ab = pd.DataFrame({
681+
"variant": ["A"] * 100 + ["B"] * 100,
682+
"converted": [1] * 35 + [0] * 65 + [1] * 20 + [0] * 80,
683+
})
684+
685+
effect_size(ab, group="variant", value="converted")
686+
```
687+
688+
```text
689+
comparison cohen_h lift_pct interpretation
690+
A vs B 0.34 75.0 medium
691+
```
692+
693+
The `interpretation` column labels the magnitude (negligible / small / medium / large), so a raw number is never left without context.
694+
695+
---
696+
499697
## `difference`
500698

501699
Symmetric percentage difference between two values or two columns: how *far apart* they are, regardless of direction. (Reach for `change` when direction matters.)

docs/index.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ One import, one line. A clean, sorted DataFrame you can read or feed straight in
6464
| `pca_variance` | How much variance does each principal component explain? |
6565
| `pca_loadings` | What does each principal component consist of? |
6666
| `imbalance` | How skewed are the classes in a target column? |
67+
| `correlate` | Which features move together, and is it significant? |
68+
| `skew_report` | How skewed is each column, and what transform helps? |
69+
| `bootstrap_ci` | What is the confidence interval for a statistic? |
70+
| `permutation_test` | Are two groups really different? (a p-value) |
71+
| `effect_size` | How big is the difference, not just whether it is significant? |
6772
| `difference` | How far apart are two values or columns (regardless of direction)? |
6873
| `split` | How does a total divide across weights or groups? |
6974
| `display` | Format numbers or a column as clean "%" strings for reports. |

percentify/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from .profiling import profiler, ProfileReport, Finding
22
from .stats import (
33
change, vif, missing, cv, outliers, pca_variance, pca_loadings, imbalance,
4+
correlate, skew_report, bootstrap_ci, permutation_test, effect_size,
45
difference, split, display, PercentifyWarning,
56
)
67

78
__all__ = [
89
"profiler", "ProfileReport", "Finding",
910
"change", "vif", "missing", "cv", "outliers", "pca_variance", "pca_loadings", "imbalance",
11+
"correlate", "skew_report", "bootstrap_ci", "permutation_test", "effect_size",
1012
"difference", "split", "display", "PercentifyWarning",
1113
]
1214
__version__ = "1.0.0"

0 commit comments

Comments
 (0)