|
1 | 1 | # Percentify |
2 | 2 |
|
3 | | -[](https://pepy.tech/project/percentify ) |
| 3 | +[](https://pepy.tech/project/percentify) |
4 | 4 | [](https://pypi.org/project/percentify/) |
5 | 5 | [](https://pypi.org/project/percentify/) |
6 | 6 | [](LICENSE) |
| 7 | +[](https://data-centt.github.io/percentify/) |
7 | 8 | [](https://github.com/data-centt/percentify/actions/workflows/python-app.yml) |
8 | 9 |
|
9 | 10 | **Percentify is a niche data science library for practitioners and learners alike, drawing its main dependencies from pandas and numpy, and including everyday statistics.** |
10 | 11 |
|
11 | | -Following the **Pareto principle**, Percentify brings the 20% of operations that make up 80% of daily data work to the forefront, each as a single, readable function call. No more digging through six-line recipes and hard-to-remember import paths for the checks you run on every dataset. |
| 12 | +Following the **Pareto principle**, Percentify brings the 20% of operations that make up 80% of daily data work to the forefront — each as a single, readable function call. It does not aim to compete with pandas, scipy, statsmodels, or scikit-learn; it stands on their shoulders and works alongside them, naming the library each function draws from so you always know where to dig deeper. |
12 | 13 |
|
13 | | -Percentify **does not aim to compete** with pandas, scipy, statsmodels, or scikit-learn; it stands on their shoulders and works *alongside* them. The goal is to make the core concepts easy to learn, quick to use, and simple to remember. Every function names the underlying library it draws from, so the moment you need the full, configurable version, you know exactly where to go. |
| 14 | +## 📖 Documentation |
14 | 15 |
|
15 | | -Every function takes a pandas `DataFrame` (or `Series`) and hands back a clean `DataFrame` you can read, sort, or feed straight into the next step. |
16 | | - |
17 | | ---- |
| 16 | +**Full guide, every function, and live examples → [data-centt.github.io/percentify](https://data-centt.github.io/percentify/)** |
18 | 17 |
|
19 | 18 | ## 📦 Installation |
20 | | -``` |
| 19 | + |
| 20 | +```bash |
21 | 21 | pip install percentify |
22 | 22 | ``` |
23 | 23 |
|
24 | 24 | Requires `numpy` and `pandas`. |
25 | 25 |
|
26 | | ---- |
27 | | - |
28 | | -## 📊 The Toolkit |
29 | | - |
30 | | -### `change`: Percentage Change |
31 | | -Two numbers, two columns, or a whole series at once. |
32 | | -```python |
33 | | -from percentify import change |
34 | | - |
35 | | -change(100, 150) # → 50.0 (a 50% increase) |
| 26 | +## Quick example |
36 | 27 |
|
37 | | -change(df["forecast"], df["actual"]) # element-wise % change between two columns |
38 | | - |
39 | | -change(df["revenue"]) # period-over-period % change down the column |
40 | | -# 0 NaN |
41 | | -# 1 50.0 |
42 | | -# 2 -40.0 |
43 | | - |
44 | | -change(df) # every numeric column at once |
45 | | -``` |
46 | | - |
47 | | -### `vif`: Variance Inflation Factor (Multicollinearity) |
48 | | -The classic multicollinearity check, without the six-line loop. |
49 | | -> _Similar Concept:_ `statsmodels.stats.outliers_influence.variance_inflation_factor` |
50 | | -```python |
51 | | -from percentify import vif |
52 | | - |
53 | | -vif(df) |
54 | | -# feature VIF |
55 | | -# 0 income 8.40 |
56 | | -# 1 debt 7.90 |
57 | | -# 2 age 1.20 |
58 | | - |
59 | | -vif(df, flag=5.0) # only rows above the threshold (your problem columns) |
60 | | -``` |
61 | | - |
62 | | -### `missing`: Missing Data Profiling |
63 | | -No more `df.isnull().sum() / len(df) * 100`. |
64 | | -> _Underlying library:_ `pandas.DataFrame.isna` |
65 | 28 | ```python |
| 29 | +import pandas as pd |
66 | 30 | from percentify import missing |
67 | 31 |
|
| 32 | +df = pd.DataFrame({ |
| 33 | + "salary": [50000, None, 60000, None], |
| 34 | + "age": [25, 30, None, 40], |
| 35 | + "city": ["NY", "LA", "SF", "LA"], |
| 36 | +}) |
| 37 | + |
68 | 38 | missing(df) |
69 | 39 | # column missing_pct |
70 | | -# 0 salary 12.40 |
71 | | -# 1 age 3.10 |
72 | | -# 2 name 0.00 |
| 40 | +# 0 salary 50.0 |
| 41 | +# 1 age 25.0 |
| 42 | +# 2 city 0.0 |
73 | 43 | ``` |
74 | 44 |
|
75 | | -### `cv`: Coefficient of Variation |
76 | | -Relative variability (std ÷ mean), as a percentage. |
77 | | -> _Underlying library:_ `scipy.stats.variation` |
78 | | -```python |
79 | | -from percentify import cv |
| 45 | +One import, one line — a clean, sorted DataFrame you can read or feed into the next step. |
80 | 46 |
|
81 | | -cv(df["salary"]) # → 34.2 (a single Series returns a number) |
82 | | -cv(df) # → DataFrame of every numeric column, most variable first |
83 | | -``` |
| 47 | +## What's inside |
84 | 48 |
|
85 | | -### `outliers`: Percentage of Outliers (IQR Method) |
86 | | -Stop rewriting the IQR bounds from scratch. |
87 | | -> _Similar Concept:_ `scipy.stats.iqr` |
88 | | -```python |
89 | | -from percentify import outliers |
| 49 | +| Function | What it answers | |
| 50 | +|---|---| |
| 51 | +| `change` | Growth — as numbers, columns, or a whole series | |
| 52 | +| `vif` | Which features are collinear? | |
| 53 | +| `missing` | How much of each column is missing? | |
| 54 | +| `cv` | How variable is each column, relative to its mean? | |
| 55 | +| `outliers` | What percentage of each column are outliers? | |
| 56 | +| `r_squared` | How well do predictions fit? | |
| 57 | +| `pca_variance` | How much variance does each principal component explain? | |
| 58 | +| `difference` | How far apart are two values or columns? | |
| 59 | +| `split` | How does a total divide across weights or groups? | |
| 60 | +| `display` | Format numbers or a column as clean "%" strings | |
90 | 61 |
|
91 | | -outliers(df["salary"]) # → 4.7 |
92 | | -outliers(df) # → DataFrame of every numeric column |
93 | | -``` |
94 | | - |
95 | | -### `r_squared`: R-Squared |
96 | | -```python |
97 | | -from percentify import r_squared |
98 | | - |
99 | | -r_squared(y_true, y_pred) # → 87.3 |
100 | | -``` |
101 | | -> _Similar Concepts:_ `sklearn.metrics.r2_score` |
102 | | -
|
103 | | -### `pca_variance`: PCA Variance Breakdown |
104 | | -Columns are standardized by default, so a feature measured in large units (e.g. |
105 | | -dollars) can't dominate the result just because of its scale. Pass |
106 | | -`standardize=False` for covariance-based PCA on the raw values. |
107 | | -> _Similar Concept:_ `sklearn.decomposition.PCA` (`.explained_variance_ratio_`) |
108 | | -```python |
109 | | -from percentify import pca_variance |
110 | | - |
111 | | -pca_variance(df) |
112 | | -# component variance_explained cumulative |
113 | | -# 0 PC1 45.2 45.2 |
114 | | -# 1 PC2 23.1 68.3 |
115 | | -# 2 PC3 12.8 81.1 |
116 | | - |
117 | | -pca_variance(df, standardize=False) # covariance-based (scale-sensitive) |
118 | | -``` |
119 | | - |
120 | | -### `difference`: Symmetric % Difference |
121 | | -How far apart two values or columns are, regardless of direction (use `change` when direction matters). |
122 | | -> _Similar Concept:_ `numpy` / `pandas` arithmetic |
123 | | -```python |
124 | | -from percentify import difference |
125 | | - |
126 | | -difference(10, 20) # → 66.67 (order-independent) |
127 | | - |
128 | | -difference(df["sensor_a"], df["sensor_b"]) # element-wise % gap between two columns |
129 | | -``` |
130 | | - |
131 | | -### `split`: Proportional Allocation |
132 | | -Distribute a total across weights or groups. |
133 | | -> _Similar Concept:_ `numpy` / `pandas` arithmetic |
134 | | -```python |
135 | | -from percentify import split |
136 | | - |
137 | | -split(10000, [2, 3, 5]) # → [2000.0, 3000.0, 5000.0] |
138 | | - |
139 | | -split(10000, df["population"]) # allocate a budget by population (aligned Series) |
140 | | -``` |
141 | | - |
142 | | -### `display`: Percentage Formatting |
143 | | -Turn numbers or a whole column into clean "%" strings for reports. |
144 | | -> _Similar Concept:_ `pandas.Series.map` + string formatting |
145 | | -```python |
146 | | -from percentify import display |
147 | | - |
148 | | -display(0.45, multiply=True) # → "45.0%" |
149 | | - |
150 | | -display(df["conv_rate"], multiply=True) # → column of "4.5%" strings |
151 | | -``` |
152 | | - |
153 | | ---- |
| 62 | +→ See the **[documentation](https://data-centt.github.io/percentify/)** for a worked, real-output example of every function. |
154 | 63 |
|
155 | 64 | ## 🛟 Friendly by design |
156 | 65 |
|
157 | | -Built for early-career analysts as much as seasoned ones: |
158 | | - |
159 | | -- **No cryptic tracebacks.** Hand it a text column where numbers are needed and you get a clear `PercentifyWarning` ("numeric columns required") plus an empty result — not an Arrow/NumPy stack trace. |
160 | | -- **Sensible defaults.** Results come back sorted worst-first (most collinear, most missing, most variable), and PCA is standardized out of the box. |
| 66 | +- **No cryptic tracebacks.** Hand a function a text column where numbers are needed and you get a clear `PercentifyWarning`, not an Arrow/NumPy stack trace. |
| 67 | +- **Sensible defaults.** Results come back sorted worst-first, and PCA is standardized out of the box. |
161 | 68 | - **DataFrames everywhere**, so the output drops straight into your notebook, your next filter, or your model. |
162 | 69 |
|
163 | | -Catch or silence the warnings like any other: |
164 | | -```python |
165 | | -import warnings |
166 | | -from percentify import PercentifyWarning |
167 | | - |
168 | | -warnings.filterwarnings("ignore", category=PercentifyWarning) |
169 | | -``` |
170 | | - |
171 | | ---- |
172 | | - |
173 | 70 | ## 🤝 Contributing |
174 | 71 |
|
175 | 72 | Contributions are welcome — but they must follow the repo's guiding principle: |
176 | 73 |
|
177 | 74 | > **Keep each method as direct-to-output as possible.** A percentify function should return the single most common answer in one line, and point users to the underlying library (pandas, scipy, statsmodels, scikit-learn) for the full, configurable version when the simplest output isn't what they're after. |
178 | 75 |
|
179 | 76 | If your idea keeps things that simple and direct: |
| 77 | + |
180 | 78 | - Open an issue first to discuss it |
181 | 79 | - Fork the repo |
182 | 80 | - Create a branch |
183 | 81 | - Commit your changes |
184 | 82 | - Open a pull request |
185 | 83 |
|
186 | | -> Anything that adds knobs and options for their own sake, or duplicates what the parent libraries already do well, is out of scope please those cases should point to the source library instead. |
| 84 | +Anything that adds knobs and options for their own sake, or duplicates what the parent libraries already do well, is out of scope — those cases should point to the source library instead. |
0 commit comments