tangra-dataforge 0.1.0a1__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 (35) hide show
  1. tangra_dataforge-0.1.0a1/.github/workflows/release.yml +53 -0
  2. tangra_dataforge-0.1.0a1/.github/workflows/tests.yml +33 -0
  3. tangra_dataforge-0.1.0a1/.gitignore +9 -0
  4. tangra_dataforge-0.1.0a1/LICENSE +21 -0
  5. tangra_dataforge-0.1.0a1/PKG-INFO +131 -0
  6. tangra_dataforge-0.1.0a1/README.md +99 -0
  7. tangra_dataforge-0.1.0a1/pyproject.toml +56 -0
  8. tangra_dataforge-0.1.0a1/src/tangra_dataforge/__init__.py +0 -0
  9. tangra_dataforge-0.1.0a1/src/tangra_dataforge/analyze.py +83 -0
  10. tangra_dataforge-0.1.0a1/src/tangra_dataforge/browser.py +0 -0
  11. tangra_dataforge-0.1.0a1/src/tangra_dataforge/clean.py +13 -0
  12. tangra_dataforge-0.1.0a1/src/tangra_dataforge/collect.py +66 -0
  13. tangra_dataforge-0.1.0a1/src/tangra_dataforge/database.py +48 -0
  14. tangra_dataforge-0.1.0a1/src/tangra_dataforge/geospatial.py +0 -0
  15. tangra_dataforge-0.1.0a1/src/tangra_dataforge/interactive.py +77 -0
  16. tangra_dataforge-0.1.0a1/src/tangra_dataforge/pipeline.py +121 -0
  17. tangra_dataforge-0.1.0a1/src/tangra_dataforge/provenance.py +53 -0
  18. tangra_dataforge-0.1.0a1/src/tangra_dataforge/scrape.py +90 -0
  19. tangra_dataforge-0.1.0a1/src/tangra_dataforge/statistics.py +158 -0
  20. tangra_dataforge-0.1.0a1/src/tangra_dataforge/validate.py +128 -0
  21. tangra_dataforge-0.1.0a1/src/tangra_dataforge/visualize.py +90 -0
  22. tangra_dataforge-0.1.0a1/tests/__init__.py +0 -0
  23. tangra_dataforge-0.1.0a1/tests/test_analyze.py +88 -0
  24. tangra_dataforge-0.1.0a1/tests/test_browser.py +0 -0
  25. tangra_dataforge-0.1.0a1/tests/test_clean.py +25 -0
  26. tangra_dataforge-0.1.0a1/tests/test_collect.py +93 -0
  27. tangra_dataforge-0.1.0a1/tests/test_database.py +66 -0
  28. tangra_dataforge-0.1.0a1/tests/test_geospatial.py +0 -0
  29. tangra_dataforge-0.1.0a1/tests/test_interactive.py +70 -0
  30. tangra_dataforge-0.1.0a1/tests/test_pipeline.py +110 -0
  31. tangra_dataforge-0.1.0a1/tests/test_provenance.py +45 -0
  32. tangra_dataforge-0.1.0a1/tests/test_scrape.py +87 -0
  33. tangra_dataforge-0.1.0a1/tests/test_statistics.py +91 -0
  34. tangra_dataforge-0.1.0a1/tests/test_validate.py +118 -0
  35. tangra_dataforge-0.1.0a1/tests/test_visualize.py +59 -0
@@ -0,0 +1,53 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.14"
18
+
19
+ - name: Install build tools
20
+ run: python -m pip install build twine
21
+
22
+ - name: Build distributions
23
+ run: python -m build
24
+
25
+ - name: Verify distributions
26
+ run: python -m twine check dist/*
27
+
28
+ - name: Upload distributions
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: python-package-distributions
32
+ path: dist/
33
+
34
+ publish:
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+
38
+ environment:
39
+ name: pypi
40
+ url: https://pypi.org/project/tangra-dataforge/
41
+
42
+ permissions:
43
+ id-token: write
44
+
45
+ steps:
46
+ - name: Download distributions
47
+ uses: actions/download-artifact@v4
48
+ with:
49
+ name: python-package-distributions
50
+ path: dist/
51
+
52
+ - name: Publish to PyPI
53
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,33 @@
1
+ name: Python Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11", "3.14"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ python -m pip install -e ".[dev,scraping,analytics,visualization]"
28
+
29
+ - name: Run tests
30
+ run: python -m pytest -v
31
+
32
+ - name: Verify dependencies
33
+ run: python -m pip check
@@ -0,0 +1,9 @@
1
+
2
+ # Python generated files
3
+ __pycache__/
4
+ *.py[cod]
5
+ *.egg-info/
6
+ .pytest_cache/
7
+ dist/
8
+ build/
9
+ .venv/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Irena Popova
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
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
+ OR 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
21
+ THE SOFTWARE.
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.5
2
+ Name: tangra-dataforge
3
+ Version: 0.1.0a1
4
+ Summary: Python toolkit for data collection, cleaning, analysis, and investigative journalism.
5
+ Author: Irena Popova
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: matplotlib>=3.8
10
+ Requires-Dist: openpyxl>=3.1
11
+ Requires-Dist: pandas>=2.0
12
+ Requires-Dist: requests>=2.31
13
+ Provides-Extra: analytics
14
+ Requires-Dist: duckdb>=1.1; extra == 'analytics'
15
+ Requires-Dist: pyarrow>=17.0; extra == 'analytics'
16
+ Requires-Dist: scipy>=1.13; extra == 'analytics'
17
+ Provides-Extra: dev
18
+ Requires-Dist: build>=1.0; extra == 'dev'
19
+ Requires-Dist: mypy>=1.14; extra == 'dev'
20
+ Requires-Dist: pytest>=8.0; extra == 'dev'
21
+ Requires-Dist: ruff>=0.9; extra == 'dev'
22
+ Provides-Extra: scraping
23
+ Requires-Dist: beautifulsoup4>=4.12; extra == 'scraping'
24
+ Requires-Dist: httpx>=0.27; extra == 'scraping'
25
+ Requires-Dist: lxml>=5.0; extra == 'scraping'
26
+ Requires-Dist: playwright>=1.50; extra == 'scraping'
27
+ Requires-Dist: scrapy>=2.12; extra == 'scraping'
28
+ Provides-Extra: visualization
29
+ Requires-Dist: geopandas>=1.0; extra == 'visualization'
30
+ Requires-Dist: plotly>=5.24; extra == 'visualization'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # TANGRA DataForge
34
+
35
+ **Open-Source Python Toolkit for Data Engineering, Analysis & Investigative Journalism**
36
+
37
+ TANGRA DataForge is an open-source Python project focused on collecting, processing, analyzing, and visualizing data. It is designed for data journalism, investigative research, and reproducible data analysis.
38
+
39
+ The project aims to transform raw data from public datasets, websites, and APIs into structured, verifiable, and meaningful information.
40
+
41
+ ## Project Goals
42
+
43
+ - Collect data from public APIs, websites, CSV, JSON, and Excel files.
44
+ - Extract structured information through web scraping.
45
+ - Clean, validate, and transform datasets using Python and Pandas.
46
+ - Perform statistical analysis and identify patterns, trends, and anomalies.
47
+ - Create informative visualizations and charts.
48
+ - Support reproducible investigations with transparent methodologies and documented sources.
49
+
50
+ ## Technology Stack
51
+
52
+ - **Python** — Core programming language
53
+ - **Pandas** — Data manipulation and analysis
54
+ - **NumPy** — Numerical computing
55
+ - **Beautiful Soup** — HTML parsing and web scraping
56
+ - **Requests** — HTTP requests and API integration
57
+ - **Matplotlib / Plotly** — Data visualization
58
+ - **pytest** — Automated testing
59
+ - **Git / GitHub** — Version control and collaboration
60
+
61
+ These technologies represent the planned toolkit. Dependencies will be introduced as features are implemented.
62
+
63
+ ## Planned Architecture
64
+
65
+ ```text
66
+ tangra-dataforge/
67
+ ├── src/
68
+ │ └── tangra_dataforge/
69
+ │ ├── __init__.py
70
+ │ ├── collect.py
71
+ │ ├── clean.py
72
+ │ ├── analyze.py
73
+ │ └── visualize.py
74
+ ├── investigations/
75
+ ├── examples/
76
+ ├── tests/
77
+ ├── pyproject.toml
78
+ ├── README.md
79
+ └── .gitignore
80
+ ```
81
+
82
+ ## Data Journalism & Research
83
+
84
+ TANGRA DataForge is being developed to support data-driven investigations into economic, social, political, and technological developments.
85
+
86
+ Planned research applications include:
87
+
88
+ - Economic indicators and regional inequality
89
+ - Housing affordability and income development
90
+ - Labour markets and workforce shortages
91
+ - Education and professional qualifications
92
+ - Technology, digital transformation, and society
93
+
94
+ Investigations will prioritize publicly accessible data, source attribution, transparent analysis, and reproducible results.
95
+
96
+ ## Development Status
97
+
98
+ **Early development — project initialization**
99
+
100
+ The toolkit is currently being established. Its first development phase will focus on Python/Pandas data processing, dataset validation, and reproducible analysis workflows.
101
+
102
+ The project is not yet available as an installable Python package.
103
+
104
+ ## Roadmap
105
+
106
+ - [ ] Establish Python package structure
107
+ - [ ] Implement CSV and JSON data loading
108
+ - [ ] Develop Pandas-based cleaning functions
109
+ - [ ] Add data validation and descriptive statistics
110
+ - [ ] Integrate API data collection
111
+ - [ ] Implement web scraping components
112
+ - [ ] Add visualization utilities
113
+ - [ ] Create a reproducible journalism case study
114
+ - [ ] Add automated tests and documentation
115
+ - [ ] Publish an initial release on PyPI
116
+
117
+ ## Responsible Data Collection
118
+
119
+ Data collection functionality will be developed with attention to applicable laws, data protection, website access policies, rate limits, and source attribution.
120
+
121
+ ## Author
122
+
123
+ **Irena Popova**
124
+
125
+ Independent Researcher | Python & Full-Stack Developer | Author & Publisher
126
+
127
+ [Website](https://www.irena-popova.com) | [GitHub](https://github.com/irenapopova)
128
+
129
+ ## License
130
+
131
+ An open-source license will be added before the first public software release.
@@ -0,0 +1,99 @@
1
+ # TANGRA DataForge
2
+
3
+ **Open-Source Python Toolkit for Data Engineering, Analysis & Investigative Journalism**
4
+
5
+ TANGRA DataForge is an open-source Python project focused on collecting, processing, analyzing, and visualizing data. It is designed for data journalism, investigative research, and reproducible data analysis.
6
+
7
+ The project aims to transform raw data from public datasets, websites, and APIs into structured, verifiable, and meaningful information.
8
+
9
+ ## Project Goals
10
+
11
+ - Collect data from public APIs, websites, CSV, JSON, and Excel files.
12
+ - Extract structured information through web scraping.
13
+ - Clean, validate, and transform datasets using Python and Pandas.
14
+ - Perform statistical analysis and identify patterns, trends, and anomalies.
15
+ - Create informative visualizations and charts.
16
+ - Support reproducible investigations with transparent methodologies and documented sources.
17
+
18
+ ## Technology Stack
19
+
20
+ - **Python** — Core programming language
21
+ - **Pandas** — Data manipulation and analysis
22
+ - **NumPy** — Numerical computing
23
+ - **Beautiful Soup** — HTML parsing and web scraping
24
+ - **Requests** — HTTP requests and API integration
25
+ - **Matplotlib / Plotly** — Data visualization
26
+ - **pytest** — Automated testing
27
+ - **Git / GitHub** — Version control and collaboration
28
+
29
+ These technologies represent the planned toolkit. Dependencies will be introduced as features are implemented.
30
+
31
+ ## Planned Architecture
32
+
33
+ ```text
34
+ tangra-dataforge/
35
+ ├── src/
36
+ │ └── tangra_dataforge/
37
+ │ ├── __init__.py
38
+ │ ├── collect.py
39
+ │ ├── clean.py
40
+ │ ├── analyze.py
41
+ │ └── visualize.py
42
+ ├── investigations/
43
+ ├── examples/
44
+ ├── tests/
45
+ ├── pyproject.toml
46
+ ├── README.md
47
+ └── .gitignore
48
+ ```
49
+
50
+ ## Data Journalism & Research
51
+
52
+ TANGRA DataForge is being developed to support data-driven investigations into economic, social, political, and technological developments.
53
+
54
+ Planned research applications include:
55
+
56
+ - Economic indicators and regional inequality
57
+ - Housing affordability and income development
58
+ - Labour markets and workforce shortages
59
+ - Education and professional qualifications
60
+ - Technology, digital transformation, and society
61
+
62
+ Investigations will prioritize publicly accessible data, source attribution, transparent analysis, and reproducible results.
63
+
64
+ ## Development Status
65
+
66
+ **Early development — project initialization**
67
+
68
+ The toolkit is currently being established. Its first development phase will focus on Python/Pandas data processing, dataset validation, and reproducible analysis workflows.
69
+
70
+ The project is not yet available as an installable Python package.
71
+
72
+ ## Roadmap
73
+
74
+ - [ ] Establish Python package structure
75
+ - [ ] Implement CSV and JSON data loading
76
+ - [ ] Develop Pandas-based cleaning functions
77
+ - [ ] Add data validation and descriptive statistics
78
+ - [ ] Integrate API data collection
79
+ - [ ] Implement web scraping components
80
+ - [ ] Add visualization utilities
81
+ - [ ] Create a reproducible journalism case study
82
+ - [ ] Add automated tests and documentation
83
+ - [ ] Publish an initial release on PyPI
84
+
85
+ ## Responsible Data Collection
86
+
87
+ Data collection functionality will be developed with attention to applicable laws, data protection, website access policies, rate limits, and source attribution.
88
+
89
+ ## Author
90
+
91
+ **Irena Popova**
92
+
93
+ Independent Researcher | Python & Full-Stack Developer | Author & Publisher
94
+
95
+ [Website](https://www.irena-popova.com) | [GitHub](https://github.com/irenapopova)
96
+
97
+ ## License
98
+
99
+ An open-source license will be added before the first public software release.
@@ -0,0 +1,56 @@
1
+
2
+
3
+ [build-system]
4
+ requires = ["hatchling>=1.26"]
5
+ build-backend = "hatchling.build"
6
+
7
+ [project]
8
+ name = "tangra-dataforge"
9
+ version = "0.1.0a1"
10
+ description = "Python toolkit for data collection, cleaning, analysis, and investigative journalism."
11
+ readme = "README.md"
12
+ requires-python = ">=3.11"
13
+ license = "MIT"
14
+ authors = [
15
+ { name = "Irena Popova" }
16
+ ]
17
+
18
+ dependencies = [
19
+ "pandas>=2.0",
20
+ "requests>=2.31",
21
+ "openpyxl>=3.1",
22
+ "matplotlib>=3.8"
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ scraping = [
27
+ "scrapy>=2.12",
28
+ "beautifulsoup4>=4.12",
29
+ "lxml>=5.0",
30
+ "httpx>=0.27",
31
+ "playwright>=1.50"
32
+ ]
33
+
34
+ analytics = [
35
+ "scipy>=1.13",
36
+ "duckdb>=1.1",
37
+ "pyarrow>=17.0"
38
+ ]
39
+
40
+ visualization = [
41
+ "plotly>=5.24",
42
+ "geopandas>=1.0"
43
+ ]
44
+
45
+ dev = [
46
+ "pytest>=8.0",
47
+ "build>=1.0",
48
+ "ruff>=0.9",
49
+ "mypy>=1.14"
50
+ ]
51
+
52
+ [tool.pytest.ini_options]
53
+ testpaths = ["tests"]
54
+
55
+ [tool.hatch.build.targets.wheel]
56
+ packages = ["src/tangra_dataforge"]
@@ -0,0 +1,83 @@
1
+
2
+ """Statistical analysis utilities for TANGRA DataForge."""
3
+
4
+ import pandas as pd
5
+
6
+
7
+ def describe_data(df: pd.DataFrame) -> pd.DataFrame:
8
+ """Calculate descriptive statistics for numeric columns."""
9
+ return df.describe()
10
+
11
+
12
+ def calculate_percentage_change(
13
+ df: pd.DataFrame,
14
+ column: str,
15
+ periods: int = 1,
16
+ ) -> pd.Series:
17
+ """Calculate percentage change between observations."""
18
+ if column not in df.columns:
19
+ raise KeyError(f"Column not found: {column}")
20
+
21
+ if periods < 1:
22
+ raise ValueError("periods must be at least 1")
23
+
24
+ return df[column].pct_change(periods=periods) * 100
25
+
26
+
27
+ def calculate_growth_rate(
28
+ initial_value: float,
29
+ final_value: float,
30
+ ) -> float:
31
+ """Calculate growth between two values as a percentage."""
32
+ if initial_value == 0:
33
+ raise ValueError("Initial value cannot be zero.")
34
+
35
+ return (
36
+ (final_value - initial_value)
37
+ / abs(initial_value)
38
+ ) * 100
39
+
40
+
41
+ def compare_columns(
42
+ df: pd.DataFrame,
43
+ column_a: str,
44
+ column_b: str,
45
+ ) -> pd.DataFrame:
46
+ """Compare two numeric columns using absolute differences."""
47
+ if column_a not in df.columns or column_b not in df.columns:
48
+ raise KeyError("One or both columns were not found.")
49
+
50
+ result = df[[column_a, column_b]].copy()
51
+ result["difference"] = result[column_a] - result[column_b]
52
+
53
+ return result
54
+
55
+
56
+ def detect_outliers(
57
+ df: pd.DataFrame,
58
+ column: str,
59
+ threshold: float = 1.5,
60
+ ) -> pd.DataFrame:
61
+ """Detect outliers using the interquartile range (IQR)."""
62
+ if column not in df.columns:
63
+ raise KeyError(f"Column not found: {column}")
64
+
65
+ if threshold <= 0:
66
+ raise ValueError("threshold must be positive")
67
+
68
+ values = df[column].dropna()
69
+
70
+ if values.empty:
71
+ return df.iloc[0:0].copy()
72
+
73
+ q1 = values.quantile(0.25)
74
+ q3 = values.quantile(0.75)
75
+ iqr = q3 - q1
76
+
77
+ lower_bound = q1 - threshold * iqr
78
+ upper_bound = q3 + threshold * iqr
79
+
80
+ return df[
81
+ (df[column] < lower_bound)
82
+ | (df[column] > upper_bound)
83
+ ].copy()
@@ -0,0 +1,13 @@
1
+ """Data cleaning utilities for TANGRA DataForge."""
2
+
3
+ import pandas as pd
4
+
5
+
6
+ def remove_duplicates(df: pd.DataFrame) -> pd.DataFrame:
7
+ """Remove duplicate rows and reset the index."""
8
+ return df.drop_duplicates().reset_index(drop=True)
9
+
10
+
11
+ def remove_empty_rows(df: pd.DataFrame) -> pd.DataFrame:
12
+ """Remove rows where all values are missing."""
13
+ return df.dropna(how="all").reset_index(drop=True)
@@ -0,0 +1,66 @@
1
+ """Data collection utilities for TANGRA DataForge."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ import pandas as pd
7
+
8
+
9
+ def load_csv(
10
+ path: str | Path,
11
+ **kwargs: Any,
12
+ ) -> pd.DataFrame:
13
+ """Load a CSV file into a Pandas DataFrame."""
14
+ return pd.read_csv(path, **kwargs)
15
+
16
+
17
+ def load_json(
18
+ path: str | Path,
19
+ **kwargs: Any,
20
+ ) -> pd.DataFrame:
21
+ """Load a JSON file into a Pandas DataFrame."""
22
+ return pd.read_json(path, **kwargs)
23
+
24
+
25
+ def load_excel(
26
+ path: str | Path,
27
+ sheet_name: str | int = 0,
28
+ **kwargs: Any,
29
+ ) -> pd.DataFrame:
30
+ """Load one Excel worksheet into a DataFrame."""
31
+ return pd.read_excel(
32
+ path,
33
+ sheet_name=sheet_name,
34
+ **kwargs,
35
+ )
36
+
37
+
38
+ def fetch_api(
39
+ url: str,
40
+ *,
41
+ timeout: float = 15.0,
42
+ params: dict[str, Any] | None = None,
43
+ ) -> pd.DataFrame:
44
+ """Fetch a JSON array of records from a public API.
45
+
46
+ The endpoint must return a JSON list of objects.
47
+ """
48
+ import requests
49
+
50
+ response = requests.get(
51
+ url,
52
+ params=params,
53
+ timeout=timeout,
54
+ )
55
+ response.raise_for_status()
56
+
57
+ data = response.json()
58
+
59
+ if not isinstance(data, list) or not all(
60
+ isinstance(item, dict) for item in data
61
+ ):
62
+ raise ValueError(
63
+ "API response must be a JSON list of objects."
64
+ )
65
+
66
+ return pd.DataFrame(data)
@@ -0,0 +1,48 @@
1
+
2
+
3
+ """DuckDB integration for TANGRA DataForge.
4
+
5
+ Query Pandas DataFrames, CSV files, and Parquet datasets
6
+ using SQL.
7
+ """
8
+
9
+ from pathlib import Path
10
+
11
+ import duckdb
12
+ import pandas as pd
13
+
14
+
15
+ def query_dataframe(
16
+ df: pd.DataFrame,
17
+ sql: str,
18
+ ) -> pd.DataFrame:
19
+ """Query a DataFrame registered as 'data'."""
20
+ with duckdb.connect(database=":memory:") as con:
21
+ con.register("data", df)
22
+ return con.execute(sql).fetchdf()
23
+
24
+
25
+ def query_csv(
26
+ path: str | Path,
27
+ sql: str,
28
+ ) -> pd.DataFrame:
29
+ """Query a CSV file registered as 'data'."""
30
+ path = Path(path)
31
+
32
+ with duckdb.connect(database=":memory:") as con:
33
+ data = con.read_csv(str(path))
34
+ con.register("data", data)
35
+ return con.execute(sql).fetchdf()
36
+
37
+
38
+ def query_parquet(
39
+ path: str | Path,
40
+ sql: str,
41
+ ) -> pd.DataFrame:
42
+ """Query a Parquet file registered as 'data'."""
43
+ path = Path(path)
44
+
45
+ with duckdb.connect(database=":memory:") as con:
46
+ data = con.read_parquet(str(path))
47
+ con.register("data", data)
48
+ return con.execute(sql).fetchdf()