tablofy 1.0.0a0__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.
Files changed (52) hide show
  1. tablofy-1.0.0a0/LICENSE +21 -0
  2. tablofy-1.0.0a0/PKG-INFO +242 -0
  3. tablofy-1.0.0a0/README.md +216 -0
  4. tablofy-1.0.0a0/pyproject.toml +51 -0
  5. tablofy-1.0.0a0/setup.cfg +4 -0
  6. tablofy-1.0.0a0/src/tablofy/__init__.py +28 -0
  7. tablofy-1.0.0a0/src/tablofy/analytics/__init__.py +1 -0
  8. tablofy-1.0.0a0/src/tablofy/analytics/insights.py +130 -0
  9. tablofy-1.0.0a0/src/tablofy/analytics/outliers.py +81 -0
  10. tablofy-1.0.0a0/src/tablofy/analytics/stats.py +115 -0
  11. tablofy-1.0.0a0/src/tablofy/cleaning/__init__.py +1 -0
  12. tablofy-1.0.0a0/src/tablofy/cleaning/cleaner.py +132 -0
  13. tablofy-1.0.0a0/src/tablofy/cleaning/report.py +29 -0
  14. tablofy-1.0.0a0/src/tablofy/core/__init__.py +1 -0
  15. tablofy-1.0.0a0/src/tablofy/core/config.py +11 -0
  16. tablofy-1.0.0a0/src/tablofy/core/errors.py +25 -0
  17. tablofy-1.0.0a0/src/tablofy/core/frame.py +641 -0
  18. tablofy-1.0.0a0/src/tablofy/core/loader.py +62 -0
  19. tablofy-1.0.0a0/src/tablofy/exploration/__init__.py +1 -0
  20. tablofy-1.0.0a0/src/tablofy/exploration/profile.py +59 -0
  21. tablofy-1.0.0a0/src/tablofy/exploration/schema.py +53 -0
  22. tablofy-1.0.0a0/src/tablofy/exploration/summary.py +32 -0
  23. tablofy-1.0.0a0/src/tablofy/reports/__init__.py +1 -0
  24. tablofy-1.0.0a0/src/tablofy/reports/excel.py +65 -0
  25. tablofy-1.0.0a0/src/tablofy/reports/html.py +71 -0
  26. tablofy-1.0.0a0/src/tablofy/sql/__init__.py +1 -0
  27. tablofy-1.0.0a0/src/tablofy/sql/duckdb_query.py +77 -0
  28. tablofy-1.0.0a0/src/tablofy/transform/__init__.py +1 -0
  29. tablofy-1.0.0a0/src/tablofy/transform/group.py +71 -0
  30. tablofy-1.0.0a0/src/tablofy/transform/operations.py +213 -0
  31. tablofy-1.0.0a0/src/tablofy/transform/safe_filter.py +76 -0
  32. tablofy-1.0.0a0/src/tablofy/utils/__init__.py +1 -0
  33. tablofy-1.0.0a0/src/tablofy/utils/formatting.py +50 -0
  34. tablofy-1.0.0a0/src/tablofy/utils/lazy_imports.py +23 -0
  35. tablofy-1.0.0a0/src/tablofy/utils/validation.py +70 -0
  36. tablofy-1.0.0a0/src/tablofy/visualization/__init__.py +1 -0
  37. tablofy-1.0.0a0/src/tablofy/visualization/charts.py +130 -0
  38. tablofy-1.0.0a0/src/tablofy/visualization/smart_chart.py +72 -0
  39. tablofy-1.0.0a0/src/tablofy.egg-info/PKG-INFO +242 -0
  40. tablofy-1.0.0a0/src/tablofy.egg-info/SOURCES.txt +50 -0
  41. tablofy-1.0.0a0/src/tablofy.egg-info/dependency_links.txt +1 -0
  42. tablofy-1.0.0a0/src/tablofy.egg-info/requires.txt +16 -0
  43. tablofy-1.0.0a0/src/tablofy.egg-info/top_level.txt +1 -0
  44. tablofy-1.0.0a0/tests/test_analytics.py +199 -0
  45. tablofy-1.0.0a0/tests/test_cleaning.py +187 -0
  46. tablofy-1.0.0a0/tests/test_exploration.py +139 -0
  47. tablofy-1.0.0a0/tests/test_loader.py +111 -0
  48. tablofy-1.0.0a0/tests/test_reports.py +33 -0
  49. tablofy-1.0.0a0/tests/test_sql.py +73 -0
  50. tablofy-1.0.0a0/tests/test_table.py +62 -0
  51. tablofy-1.0.0a0/tests/test_transform.py +309 -0
  52. tablofy-1.0.0a0/tests/test_visualization.py +171 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tablofy Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,242 @@
1
+ Metadata-Version: 2.4
2
+ Name: tablofy
3
+ Version: 1.0.0a0
4
+ Summary: A beginner-friendly data analytics library wrapping pandas, matplotlib, seaborn, and DuckDB.
5
+ Author: Tablofy Contributors
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: pandas>=2.0
11
+ Requires-Dist: numpy>=1.24
12
+ Requires-Dist: matplotlib>=3.7
13
+ Requires-Dist: seaborn>=0.12
14
+ Requires-Dist: plotly>=5.14
15
+ Requires-Dist: duckdb>=0.8
16
+ Requires-Dist: pyarrow>=12
17
+ Requires-Dist: openpyxl>=3.1
18
+ Requires-Dist: jinja2>=3.1
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=7; extra == "dev"
21
+ Requires-Dist: ruff>=0.1; extra == "dev"
22
+ Requires-Dist: black>=23; extra == "dev"
23
+ Requires-Dist: build>=1.0; extra == "dev"
24
+ Requires-Dist: twine>=4.0; extra == "dev"
25
+ Dynamic: license-file
26
+
27
+ # Tablofy
28
+
29
+ A beginner-friendly data analytics library for Python that wraps pandas, matplotlib, seaborn, and DuckDB under a simple, unified API.
30
+
31
+ ```python
32
+ import tablofy as tf
33
+
34
+ data = tf.load("sales.csv")
35
+ data.clean()
36
+ print(data.profile())
37
+ data.chart("sales by month")
38
+ print(data.insights())
39
+ data.report("report.html")
40
+ ```
41
+
42
+ ## What problem does Tablofy solve?
43
+
44
+ Pandas is powerful but has a steep learning curve. Tablofy wraps it so you can:
45
+
46
+ - **Load** CSV, Excel, JSON, and Parquet with a single function
47
+ - **Explore** your data with plain-English methods (`preview()`, `profile()`, `summary()`)
48
+ - **Clean** everything in one call (`clean()`) — duplicates, missing values, column names, dates, whitespace
49
+ - **Chart** without memorizing matplotlib or seaborn APIs
50
+ - **Run SQL** directly on a DataFrame using DuckDB
51
+ - **Report** with a single call to `report("report.html")`
52
+
53
+ No complex pandas syntax. No plotting boilerplate. No separate SQL database setup.
54
+
55
+ ## Installation
56
+
57
+ ```bash
58
+ pip install tablofy
59
+ ```
60
+
61
+ Requires Python 3.9 or later.
62
+
63
+ ## Quick Start
64
+
65
+ ```python
66
+ import tablofy as tf
67
+
68
+ # Load a file
69
+ data = tf.load("sales.csv")
70
+
71
+ # Explore
72
+ print(data.preview()) # first 5 rows
73
+ print(data.shape()) # {"rows": 100, "columns": 8}
74
+ print(data.columns()) # ["date", "region", "product", "sales"]
75
+ print(data.profile()) # full dataset profile
76
+
77
+ # Clean in one go
78
+ data.clean()
79
+
80
+ # Chart
81
+ data.bar(x="region", y="sales")
82
+ data.chart("sales by month") # plain English!
83
+
84
+ # Get automated insights
85
+ for note in data.insights():
86
+ print(f" - {note}")
87
+
88
+ # Run SQL
89
+ print(data.sql("SELECT region, SUM(sales) FROM data GROUP BY region"))
90
+
91
+ # Generate a report
92
+ data.report("report.html")
93
+ ```
94
+
95
+ ## Supported file formats
96
+
97
+ | Extension | Format | Load | Export |
98
+ |-----------|--------|------|--------|
99
+ | `.csv` | Comma-separated values | `tf.load()` | `data.export()` |
100
+ | `.xlsx` | Excel workbook | `tf.load()` | `data.export()` |
101
+ | `.xls` | Excel 97-2003 | `tf.load()` | — |
102
+ | `.json` | JSON | `tf.load()` | `data.export()` |
103
+ | `.parquet` | Apache Parquet | `tf.load()` | `data.export()` |
104
+
105
+ ## API overview
106
+
107
+ ### Loading
108
+
109
+ | Method | Description |
110
+ |--------|-------------|
111
+ | `tf.load(path)` | Load a file (auto-detects format) |
112
+ | `TablofyFrame(df, name)` | Wrap an existing pandas DataFrame |
113
+
114
+ ### Exploration
115
+
116
+ | Method | Description |
117
+ |--------|-------------|
118
+ | `.preview(n=5)` | First *n* rows |
119
+ | `.head(n=5)` | Alias for preview |
120
+ | `.shape()` | Row and column counts (dict) |
121
+ | `.columns()` | Column names (list) |
122
+ | `.dtypes` | Column -> dtype mapping (dict) |
123
+ | `.types()` | Column types and null counts (DataFrame) |
124
+ | `.missing()` | Columns with null values (DataFrame) |
125
+ | `.duplicates()` | Duplicate row stats (dict) |
126
+ | `.summary()` | Descriptive statistics (DataFrame) |
127
+ | `.profile()` | Full dataset profile (dict) |
128
+ | `.size` | Total cell count (int) |
129
+ | `len(data)` | Row count (int) |
130
+ | `.to_pandas()` | Get the underlying pandas DataFrame |
131
+
132
+ ### Cleaning
133
+
134
+ | Method | Description |
135
+ |--------|-------------|
136
+ | `.clean()` | Clean in-place: duplicates, missing, column names, dates, whitespace |
137
+ | `.clean_report()` | Report of actions from last clean (dict) |
138
+
139
+ ### Transform
140
+
141
+ | Method | Description |
142
+ |--------|-------------|
143
+ | `.select(*cols)` | New frame with only the given columns |
144
+ | `.drop(col)` | New frame without a column |
145
+ | `.rename(mapping)` | New frame with renamed columns |
146
+ | `.sort(by, descending)` | New frame sorted by a column |
147
+ | `.filter(expression)` | New frame filtered by a pandas query expression |
148
+ | `.group(by)` | A `GroupedFrame` for `.sum()` / `.mean()` aggregation |
149
+ | `.pivot(index, columns, values)` | Pivot table |
150
+ | `.join(other, on, how)` | Merge two frames on a shared key |
151
+ | `.export(path)` | Write to CSV, XLSX, JSON, or Parquet |
152
+
153
+ ### Visualization
154
+
155
+ | Method | Description |
156
+ |--------|-------------|
157
+ | `.bar(x, y, save)` | Bar chart |
158
+ | `.line(x, y, save)` | Line chart |
159
+ | `.scatter(x, y, save)` | Scatter plot |
160
+ | `.hist(col, save)` | Histogram |
161
+ | `.box(x, y, save)` | Box plot |
162
+ | `.heatmap(save)` | Correlation heatmap |
163
+ | `.pairplot(save)` | Pairwise scatter matrix |
164
+ | `.chart(description)` | Smart chart from plain English |
165
+
166
+ ### Analytics
167
+
168
+ | Method | Description |
169
+ |--------|-------------|
170
+ | `.insights()` | Rule-based observations (list of strings) |
171
+ | `.stats.describe()` | Descriptive statistics (DataFrame) |
172
+ | `.stats.correlation()` | Correlation matrix (DataFrame) |
173
+ | `.stats.covariance()` | Covariance matrix (DataFrame) |
174
+ | `.stats.outliers(col)` | IQR outlier detection |
175
+ | `.stats.mean(col)` | Mean of a column |
176
+ | `.stats.median(col)` | Median of a column |
177
+ | `.stats.std(col)` | Standard deviation |
178
+ | `.stats.min(col)` | Minimum value |
179
+ | `.stats.max(col)` | Maximum value |
180
+ | `.stats.quantile(col, q)` | Quantile value |
181
+ | `.stats.value_counts(col)` | Frequency counts (Series) |
182
+
183
+ ### SQL
184
+
185
+ | Method | Description |
186
+ |--------|-------------|
187
+ | `.sql(query)` | Run read-only SQL via DuckDB (table name: `data`) |
188
+
189
+ ### Reports
190
+
191
+ | Method | Description |
192
+ |--------|-------------|
193
+ | `.report(path)` | Generate HTML or Excel report |
194
+
195
+ ## Examples
196
+
197
+ See the [`examples/`](examples/) directory for runnable scripts:
198
+
199
+ | Script | What it shows |
200
+ |--------|---------------|
201
+ | `basic_usage.py` | Core workflow: load → inspect → clean → chart |
202
+ | `exploration_demo.py` | All exploration methods |
203
+ | `cleaning_demo.py` | Cleaning pipeline + report |
204
+ | `transform_demo.py` | Select, filter, group, sort, pivot, chain |
205
+ | `charts_demo.py` | All chart types + smart chart |
206
+ | `analytics_demo.py` | Insights + statistics |
207
+ | `sql_demo.py` | SQL queries + chaining |
208
+ | `report_demo.py` | HTML + Excel reports |
209
+
210
+ ## What Tablofy is not
211
+
212
+ - **Not a replacement for pandas** — you can always get the underlying DataFrame with `.to_pandas()`
213
+ - **Not a big-data tool** — it operates in-memory on a single machine
214
+ - **Not a machine-learning library** — no model training or prediction
215
+ - **Not a dashboard tool** — reports are static HTML or Excel files
216
+ - **Not a database** — SQL runs in-memory against the loaded DataFrame only
217
+
218
+ ## Roadmap
219
+
220
+ - [x] Load CSV, Excel, JSON, Parquet
221
+ - [x] Data exploration (profile, summary, missing, types)
222
+ - [x] Data cleaning (duplicates, missing values, column names, dates)
223
+ - [x] Transformations (select, drop, sort, filter, group, join)
224
+ - [x] Visualization (bar, line, scatter, hist, box, heatmap, pairplot)
225
+ - [x] Smart chart from plain-English descriptions
226
+ - [x] Rule-based insights
227
+ - [x] Statistical helpers (mean, median, outliers, correlation)
228
+ - [x] SQL queries via DuckDB
229
+ - [x] HTML and Excel reports
230
+ - [ ] Interactive charts with Plotly
231
+ - [ ] Time-series analysis helpers
232
+ - [ ] More chart types (area, pie, violin)
233
+ - [ ] Custom theme/style configuration
234
+ - [ ] Integration with Jupyter Notebook widgets
235
+
236
+ ## Contributing
237
+
238
+ Contributions are welcome! Open an issue or pull request on GitHub.
239
+
240
+ ## License
241
+
242
+ MIT
@@ -0,0 +1,216 @@
1
+ # Tablofy
2
+
3
+ A beginner-friendly data analytics library for Python that wraps pandas, matplotlib, seaborn, and DuckDB under a simple, unified API.
4
+
5
+ ```python
6
+ import tablofy as tf
7
+
8
+ data = tf.load("sales.csv")
9
+ data.clean()
10
+ print(data.profile())
11
+ data.chart("sales by month")
12
+ print(data.insights())
13
+ data.report("report.html")
14
+ ```
15
+
16
+ ## What problem does Tablofy solve?
17
+
18
+ Pandas is powerful but has a steep learning curve. Tablofy wraps it so you can:
19
+
20
+ - **Load** CSV, Excel, JSON, and Parquet with a single function
21
+ - **Explore** your data with plain-English methods (`preview()`, `profile()`, `summary()`)
22
+ - **Clean** everything in one call (`clean()`) — duplicates, missing values, column names, dates, whitespace
23
+ - **Chart** without memorizing matplotlib or seaborn APIs
24
+ - **Run SQL** directly on a DataFrame using DuckDB
25
+ - **Report** with a single call to `report("report.html")`
26
+
27
+ No complex pandas syntax. No plotting boilerplate. No separate SQL database setup.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install tablofy
33
+ ```
34
+
35
+ Requires Python 3.9 or later.
36
+
37
+ ## Quick Start
38
+
39
+ ```python
40
+ import tablofy as tf
41
+
42
+ # Load a file
43
+ data = tf.load("sales.csv")
44
+
45
+ # Explore
46
+ print(data.preview()) # first 5 rows
47
+ print(data.shape()) # {"rows": 100, "columns": 8}
48
+ print(data.columns()) # ["date", "region", "product", "sales"]
49
+ print(data.profile()) # full dataset profile
50
+
51
+ # Clean in one go
52
+ data.clean()
53
+
54
+ # Chart
55
+ data.bar(x="region", y="sales")
56
+ data.chart("sales by month") # plain English!
57
+
58
+ # Get automated insights
59
+ for note in data.insights():
60
+ print(f" - {note}")
61
+
62
+ # Run SQL
63
+ print(data.sql("SELECT region, SUM(sales) FROM data GROUP BY region"))
64
+
65
+ # Generate a report
66
+ data.report("report.html")
67
+ ```
68
+
69
+ ## Supported file formats
70
+
71
+ | Extension | Format | Load | Export |
72
+ |-----------|--------|------|--------|
73
+ | `.csv` | Comma-separated values | `tf.load()` | `data.export()` |
74
+ | `.xlsx` | Excel workbook | `tf.load()` | `data.export()` |
75
+ | `.xls` | Excel 97-2003 | `tf.load()` | — |
76
+ | `.json` | JSON | `tf.load()` | `data.export()` |
77
+ | `.parquet` | Apache Parquet | `tf.load()` | `data.export()` |
78
+
79
+ ## API overview
80
+
81
+ ### Loading
82
+
83
+ | Method | Description |
84
+ |--------|-------------|
85
+ | `tf.load(path)` | Load a file (auto-detects format) |
86
+ | `TablofyFrame(df, name)` | Wrap an existing pandas DataFrame |
87
+
88
+ ### Exploration
89
+
90
+ | Method | Description |
91
+ |--------|-------------|
92
+ | `.preview(n=5)` | First *n* rows |
93
+ | `.head(n=5)` | Alias for preview |
94
+ | `.shape()` | Row and column counts (dict) |
95
+ | `.columns()` | Column names (list) |
96
+ | `.dtypes` | Column -> dtype mapping (dict) |
97
+ | `.types()` | Column types and null counts (DataFrame) |
98
+ | `.missing()` | Columns with null values (DataFrame) |
99
+ | `.duplicates()` | Duplicate row stats (dict) |
100
+ | `.summary()` | Descriptive statistics (DataFrame) |
101
+ | `.profile()` | Full dataset profile (dict) |
102
+ | `.size` | Total cell count (int) |
103
+ | `len(data)` | Row count (int) |
104
+ | `.to_pandas()` | Get the underlying pandas DataFrame |
105
+
106
+ ### Cleaning
107
+
108
+ | Method | Description |
109
+ |--------|-------------|
110
+ | `.clean()` | Clean in-place: duplicates, missing, column names, dates, whitespace |
111
+ | `.clean_report()` | Report of actions from last clean (dict) |
112
+
113
+ ### Transform
114
+
115
+ | Method | Description |
116
+ |--------|-------------|
117
+ | `.select(*cols)` | New frame with only the given columns |
118
+ | `.drop(col)` | New frame without a column |
119
+ | `.rename(mapping)` | New frame with renamed columns |
120
+ | `.sort(by, descending)` | New frame sorted by a column |
121
+ | `.filter(expression)` | New frame filtered by a pandas query expression |
122
+ | `.group(by)` | A `GroupedFrame` for `.sum()` / `.mean()` aggregation |
123
+ | `.pivot(index, columns, values)` | Pivot table |
124
+ | `.join(other, on, how)` | Merge two frames on a shared key |
125
+ | `.export(path)` | Write to CSV, XLSX, JSON, or Parquet |
126
+
127
+ ### Visualization
128
+
129
+ | Method | Description |
130
+ |--------|-------------|
131
+ | `.bar(x, y, save)` | Bar chart |
132
+ | `.line(x, y, save)` | Line chart |
133
+ | `.scatter(x, y, save)` | Scatter plot |
134
+ | `.hist(col, save)` | Histogram |
135
+ | `.box(x, y, save)` | Box plot |
136
+ | `.heatmap(save)` | Correlation heatmap |
137
+ | `.pairplot(save)` | Pairwise scatter matrix |
138
+ | `.chart(description)` | Smart chart from plain English |
139
+
140
+ ### Analytics
141
+
142
+ | Method | Description |
143
+ |--------|-------------|
144
+ | `.insights()` | Rule-based observations (list of strings) |
145
+ | `.stats.describe()` | Descriptive statistics (DataFrame) |
146
+ | `.stats.correlation()` | Correlation matrix (DataFrame) |
147
+ | `.stats.covariance()` | Covariance matrix (DataFrame) |
148
+ | `.stats.outliers(col)` | IQR outlier detection |
149
+ | `.stats.mean(col)` | Mean of a column |
150
+ | `.stats.median(col)` | Median of a column |
151
+ | `.stats.std(col)` | Standard deviation |
152
+ | `.stats.min(col)` | Minimum value |
153
+ | `.stats.max(col)` | Maximum value |
154
+ | `.stats.quantile(col, q)` | Quantile value |
155
+ | `.stats.value_counts(col)` | Frequency counts (Series) |
156
+
157
+ ### SQL
158
+
159
+ | Method | Description |
160
+ |--------|-------------|
161
+ | `.sql(query)` | Run read-only SQL via DuckDB (table name: `data`) |
162
+
163
+ ### Reports
164
+
165
+ | Method | Description |
166
+ |--------|-------------|
167
+ | `.report(path)` | Generate HTML or Excel report |
168
+
169
+ ## Examples
170
+
171
+ See the [`examples/`](examples/) directory for runnable scripts:
172
+
173
+ | Script | What it shows |
174
+ |--------|---------------|
175
+ | `basic_usage.py` | Core workflow: load → inspect → clean → chart |
176
+ | `exploration_demo.py` | All exploration methods |
177
+ | `cleaning_demo.py` | Cleaning pipeline + report |
178
+ | `transform_demo.py` | Select, filter, group, sort, pivot, chain |
179
+ | `charts_demo.py` | All chart types + smart chart |
180
+ | `analytics_demo.py` | Insights + statistics |
181
+ | `sql_demo.py` | SQL queries + chaining |
182
+ | `report_demo.py` | HTML + Excel reports |
183
+
184
+ ## What Tablofy is not
185
+
186
+ - **Not a replacement for pandas** — you can always get the underlying DataFrame with `.to_pandas()`
187
+ - **Not a big-data tool** — it operates in-memory on a single machine
188
+ - **Not a machine-learning library** — no model training or prediction
189
+ - **Not a dashboard tool** — reports are static HTML or Excel files
190
+ - **Not a database** — SQL runs in-memory against the loaded DataFrame only
191
+
192
+ ## Roadmap
193
+
194
+ - [x] Load CSV, Excel, JSON, Parquet
195
+ - [x] Data exploration (profile, summary, missing, types)
196
+ - [x] Data cleaning (duplicates, missing values, column names, dates)
197
+ - [x] Transformations (select, drop, sort, filter, group, join)
198
+ - [x] Visualization (bar, line, scatter, hist, box, heatmap, pairplot)
199
+ - [x] Smart chart from plain-English descriptions
200
+ - [x] Rule-based insights
201
+ - [x] Statistical helpers (mean, median, outliers, correlation)
202
+ - [x] SQL queries via DuckDB
203
+ - [x] HTML and Excel reports
204
+ - [ ] Interactive charts with Plotly
205
+ - [ ] Time-series analysis helpers
206
+ - [ ] More chart types (area, pie, violin)
207
+ - [ ] Custom theme/style configuration
208
+ - [ ] Integration with Jupyter Notebook widgets
209
+
210
+ ## Contributing
211
+
212
+ Contributions are welcome! Open an issue or pull request on GitHub.
213
+
214
+ ## License
215
+
216
+ MIT
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tablofy"
7
+ version = "1.0.0-alpha"
8
+ description = "A beginner-friendly data analytics library wrapping pandas, matplotlib, seaborn, and DuckDB."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ authors = [
12
+ { name = "Tablofy Contributors" },
13
+ ]
14
+ requires-python = ">=3.9"
15
+ dependencies = [
16
+ "pandas>=2.0",
17
+ "numpy>=1.24",
18
+ "matplotlib>=3.7",
19
+ "seaborn>=0.12",
20
+ "plotly>=5.14",
21
+ "duckdb>=0.8",
22
+ "pyarrow>=12",
23
+ "openpyxl>=3.1",
24
+ "jinja2>=3.1",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "pytest>=7",
30
+ "ruff>=0.1",
31
+ "black>=23",
32
+ "build>=1.0",
33
+ "twine>=4.0",
34
+ ]
35
+
36
+ [tool.setuptools.packages.find]
37
+ where = ["src"]
38
+
39
+ [tool.ruff]
40
+ line-length = 100
41
+ target-version = "py39"
42
+
43
+ [tool.ruff.lint]
44
+ select = ["E", "F", "W", "I"]
45
+
46
+ [tool.black]
47
+ line-length = 100
48
+ target-version = ["py39"]
49
+
50
+ [tool.pytest.ini_options]
51
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,28 @@
1
+ """Tablofy — A beginner-friendly data analytics library."""
2
+
3
+ __version__ = "1.0.0-alpha"
4
+
5
+ from tablofy.core.errors import (
6
+ TablofyColumnError,
7
+ TablofyDataError,
8
+ TablofyError,
9
+ TablofyFileError,
10
+ )
11
+ from tablofy.core.frame import TablofyFrame
12
+ from tablofy.core.loader import load
13
+
14
+ ColumnNotFoundError = TablofyColumnError
15
+ EmptyTableError = TablofyDataError
16
+ FileFormatError = TablofyFileError
17
+
18
+ __all__ = [
19
+ "TablofyFrame",
20
+ "load",
21
+ "TablofyError",
22
+ "TablofyFileError",
23
+ "TablofyColumnError",
24
+ "TablofyDataError",
25
+ "ColumnNotFoundError",
26
+ "EmptyTableError",
27
+ "FileFormatError",
28
+ ]
@@ -0,0 +1 @@
1
+ """Analytics: insights, statistics, outlier detection."""
@@ -0,0 +1,130 @@
1
+ """Rule-based insight generation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from tablofy.core.frame import TablofyFrame
9
+
10
+
11
+ class Insights:
12
+ """Generates deterministic, rule-based observations about a dataset."""
13
+
14
+ def __init__(self, frame: TablofyFrame) -> None:
15
+ self._frame = frame
16
+ self._df = frame._df
17
+
18
+ def generate(self) -> list[str]:
19
+ """Return a list of human-readable insight strings.
20
+
21
+ Checks performed
22
+ -----------------
23
+ - Dataset dimensions
24
+ - Missing values
25
+ - Duplicate rows
26
+ - Strong correlations
27
+ - High-cardinality columns
28
+ - Low-cardinality categorical columns
29
+ - Highest/lowest numeric values
30
+ - Outlier hints
31
+ """
32
+ insights: list[str] = []
33
+ df = self._df
34
+
35
+ rows, cols = df.shape
36
+ insights.append(f"Dataset has {rows} rows and {cols} columns.")
37
+
38
+ # Missing values
39
+ missing_counts = df.isna().sum()
40
+ total_missing = int(missing_counts.sum())
41
+ if total_missing:
42
+ pct = total_missing / df.size * 100
43
+ insights.append(
44
+ f"Found {total_missing} missing cell(s) ({pct:.1f}% of data)."
45
+ )
46
+ cols_with_missing = [c for c in df.columns if missing_counts[c] > 0]
47
+ insights.append(
48
+ f"Columns with missing values: {cols_with_missing}."
49
+ )
50
+ else:
51
+ insights.append("No missing values detected.")
52
+
53
+ # Duplicate rows
54
+ dup_count = int(df.duplicated().sum())
55
+ if dup_count:
56
+ dup_pct = dup_count / rows * 100
57
+ insights.append(
58
+ f"Found {dup_count} duplicate row(s) ({dup_pct:.1f}% of rows)."
59
+ )
60
+ else:
61
+ insights.append("No duplicate rows found.")
62
+
63
+ # Strong correlations
64
+ numeric = df.select_dtypes(include="number")
65
+ if numeric.shape[1] >= 2:
66
+ corr = numeric.corr().abs()
67
+ strong: list[str] = []
68
+ for i in range(len(corr.columns)):
69
+ for j in range(i + 1, len(corr.columns)):
70
+ val = corr.iloc[i, j]
71
+ if val >= 0.7:
72
+ strong.append(
73
+ f"{corr.columns[i]} — {corr.columns[j]} "
74
+ f"(r = {corr.iloc[i, j]:.2f})"
75
+ )
76
+ if strong:
77
+ insights.append(
78
+ f"Strong correlations detected ({len(strong)}): "
79
+ f"{'; '.join(strong)}."
80
+ )
81
+ else:
82
+ insights.append(
83
+ "No strong correlations (|r| >= 0.7) found between numeric columns."
84
+ )
85
+
86
+ # High-cardinality columns
87
+ high_card = [
88
+ c for c in df.columns
89
+ if df[c].nunique() > 50 and df[c].nunique() == len(df)
90
+ ]
91
+ if high_card:
92
+ insights.append(
93
+ f"High-cardinality columns (unique ID-like): {high_card}."
94
+ )
95
+
96
+ # Low-cardinality categorical columns
97
+ obj_cols = df.select_dtypes(include=["object", "category"]).columns
98
+ low_card = [c for c in obj_cols if 2 <= df[c].nunique() <= 10]
99
+ if low_card:
100
+ insights.append(
101
+ f"Low-cardinality categorical columns ({len(low_card)}): "
102
+ f"{low_card}."
103
+ )
104
+
105
+ # Highest / lowest numeric
106
+ if numeric.shape[1] >= 1:
107
+ for col in numeric.columns:
108
+ max_val = df[col].max()
109
+ min_val = df[col].min()
110
+ insights.append(
111
+ f"{col}: min = {min_val}, max = {max_val}."
112
+ )
113
+
114
+ # Outlier hints (IQR-based)
115
+ if numeric.shape[1] >= 1:
116
+ for col in numeric.columns:
117
+ q1 = df[col].quantile(0.25)
118
+ q3 = df[col].quantile(0.75)
119
+ iqr = q3 - q1
120
+ lower = q1 - 1.5 * iqr
121
+ upper = q3 + 1.5 * iqr
122
+ outlier_count = int(((df[col] < lower) | (df[col] > upper)).sum())
123
+ if outlier_count:
124
+ pct = outlier_count / rows * 100
125
+ insights.append(
126
+ f"{col}: {outlier_count} potential outlier(s) "
127
+ f"({pct:.1f}% of rows)."
128
+ )
129
+
130
+ return insights