bidviz 1.0.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
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.
- {bidviz-1.0.0.dist-info → bidviz-1.1.0.dist-info}/METADATA +52 -4
- bidviz-1.1.0.dist-info/RECORD +32 -0
- {bidviz-1.0.0.dist-info → bidviz-1.1.0.dist-info}/WHEEL +1 -1
- {bidviz-1.0.0.dist-info → bidviz-1.1.0.dist-info}/licenses/LICENSE +0 -0
- bidviz-1.1.0.dist-info/top_level.txt +2 -0
- bidviz_polars/__init__.py +33 -0
- bidviz_polars/core/__init__.py +5 -0
- bidviz_polars/core/base.py +43 -0
- bidviz_polars/transformer.py +337 -0
- bidviz_polars/transformers/__init__.py +31 -0
- bidviz_polars/transformers/bar.py +68 -0
- bidviz_polars/transformers/heatmap.py +120 -0
- bidviz_polars/transformers/kpi.py +60 -0
- bidviz_polars/transformers/line.py +126 -0
- bidviz_polars/transformers/other.py +108 -0
- bidviz_polars/transformers/pie.py +57 -0
- bidviz_polars/transformers/table.py +48 -0
- bidviz_polars/utils.py +220 -0
- bidviz-1.0.0.dist-info/RECORD +0 -19
- bidviz-1.0.0.dist-info/top_level.txt +0 -1
bidviz_polars/utils.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for Polars data transformation and formatting.
|
|
3
|
+
|
|
4
|
+
These utilities handle Polars-specific data types and conversions,
|
|
5
|
+
leveraging Polars' high-performance API for data manipulation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, List
|
|
9
|
+
|
|
10
|
+
import polars as pl
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def safe_get_value(value: Any) -> Any:
|
|
14
|
+
"""
|
|
15
|
+
Safely extract a value from Polars objects, converting null to None.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
value: Value to extract (can be Polars type or Python type)
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Python-native value with null converted to None
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
>>> safe_get_value(None)
|
|
25
|
+
None
|
|
26
|
+
>>> safe_get_value(42)
|
|
27
|
+
42
|
|
28
|
+
>>> safe_get_value(3.14)
|
|
29
|
+
3.14
|
|
30
|
+
"""
|
|
31
|
+
if value is None:
|
|
32
|
+
return None
|
|
33
|
+
if isinstance(value, (int, float, str, bool)):
|
|
34
|
+
return value
|
|
35
|
+
# Handle Polars temporal types
|
|
36
|
+
if hasattr(value, "isoformat"): # datetime/date/time objects
|
|
37
|
+
return str(value)
|
|
38
|
+
return value
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def format_label(column_name: str) -> str:
|
|
42
|
+
"""
|
|
43
|
+
Convert snake_case column name to Title Case label.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
column_name: Column name in snake_case format
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Formatted label in Title Case
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
>>> format_label('total_gmv')
|
|
53
|
+
'Total Gmv'
|
|
54
|
+
>>> format_label('customer_id')
|
|
55
|
+
'Customer Id'
|
|
56
|
+
>>> format_label('avg_days_to_ship')
|
|
57
|
+
'Avg Days To Ship'
|
|
58
|
+
"""
|
|
59
|
+
return column_name.replace("_", " ").title()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def validate_columns(df: pl.DataFrame, required_columns: List[str]) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Validate that required columns exist in the Polars DataFrame.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
df: Polars DataFrame to validate
|
|
68
|
+
required_columns: List of required column names
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ValueError: If any required columns are missing
|
|
72
|
+
|
|
73
|
+
Examples:
|
|
74
|
+
>>> df = pl.DataFrame({'a': [1, 2], 'b': [3, 4]})
|
|
75
|
+
>>> validate_columns(df, ['a', 'b']) # No error
|
|
76
|
+
>>> validate_columns(df, ['a', 'c']) # Raises ValueError
|
|
77
|
+
Traceback (most recent call last):
|
|
78
|
+
...
|
|
79
|
+
ValueError: Missing required columns: c
|
|
80
|
+
"""
|
|
81
|
+
missing = [col for col in required_columns if col not in df.columns]
|
|
82
|
+
if missing:
|
|
83
|
+
raise ValueError(f"Missing required columns: {', '.join(missing)}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def safe_convert_to_numeric(series: pl.Series) -> pl.Series:
|
|
87
|
+
"""
|
|
88
|
+
Safely convert a Polars Series to numeric type.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
series: Series to convert
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Numeric series with errors converted to null
|
|
95
|
+
|
|
96
|
+
Examples:
|
|
97
|
+
>>> s = pl.Series(['1', '2', 'abc'])
|
|
98
|
+
>>> result = safe_convert_to_numeric(s)
|
|
99
|
+
>>> result.to_list()
|
|
100
|
+
[1.0, 2.0, None]
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
return series.cast(pl.Float64, strict=False)
|
|
104
|
+
except Exception:
|
|
105
|
+
return series
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def clean_dataframe(df: pl.DataFrame) -> pl.DataFrame:
|
|
109
|
+
"""
|
|
110
|
+
Clean DataFrame column names by converting to lowercase and replacing spaces.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
df: Polars DataFrame to clean
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
DataFrame with cleaned column names
|
|
117
|
+
|
|
118
|
+
Examples:
|
|
119
|
+
>>> df = pl.DataFrame({'Total GMV': [100], 'Customer Name': ['John']})
|
|
120
|
+
>>> clean_df = clean_dataframe(df)
|
|
121
|
+
>>> clean_df.columns
|
|
122
|
+
['total_gmv', 'customer_name']
|
|
123
|
+
"""
|
|
124
|
+
new_columns = [col.lower().replace(" ", "_") for col in df.columns]
|
|
125
|
+
return df.rename(dict(zip(df.columns, new_columns)))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_numeric_columns(df: pl.DataFrame) -> List[str]:
|
|
129
|
+
"""
|
|
130
|
+
Get list of numeric column names from Polars DataFrame.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
df: Polars DataFrame to analyze
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
List of numeric column names
|
|
137
|
+
|
|
138
|
+
Examples:
|
|
139
|
+
>>> df = pl.DataFrame({'a': [1, 2], 'b': ['x', 'y'], 'c': [1.5, 2.5]})
|
|
140
|
+
>>> get_numeric_columns(df)
|
|
141
|
+
['a', 'c']
|
|
142
|
+
"""
|
|
143
|
+
numeric_types = [
|
|
144
|
+
pl.Int8,
|
|
145
|
+
pl.Int16,
|
|
146
|
+
pl.Int32,
|
|
147
|
+
pl.Int64,
|
|
148
|
+
pl.UInt8,
|
|
149
|
+
pl.UInt16,
|
|
150
|
+
pl.UInt32,
|
|
151
|
+
pl.UInt64,
|
|
152
|
+
pl.Float32,
|
|
153
|
+
pl.Float64,
|
|
154
|
+
]
|
|
155
|
+
return [col for col in df.columns if df[col].dtype in numeric_types]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def paginate_dataframe(
|
|
159
|
+
df: pl.DataFrame, page: int = 1, page_size: int = 50
|
|
160
|
+
) -> tuple[pl.DataFrame, dict]:
|
|
161
|
+
"""
|
|
162
|
+
Paginate a Polars DataFrame and return pagination metadata.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
df: Polars DataFrame to paginate
|
|
166
|
+
page: Page number (1-indexed)
|
|
167
|
+
page_size: Number of rows per page
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Tuple of (paginated DataFrame, pagination metadata dict)
|
|
171
|
+
|
|
172
|
+
Examples:
|
|
173
|
+
>>> df = pl.DataFrame({'a': range(100)})
|
|
174
|
+
>>> page_df, meta = paginate_dataframe(df, page=2, page_size=25)
|
|
175
|
+
>>> len(page_df)
|
|
176
|
+
25
|
|
177
|
+
>>> meta['total']
|
|
178
|
+
100
|
|
179
|
+
>>> meta['page']
|
|
180
|
+
2
|
|
181
|
+
"""
|
|
182
|
+
total = len(df)
|
|
183
|
+
total_pages = (total + page_size - 1) // page_size # Ceiling division
|
|
184
|
+
|
|
185
|
+
# Ensure page is within valid range
|
|
186
|
+
page = max(1, min(page, total_pages if total_pages > 0 else 1))
|
|
187
|
+
|
|
188
|
+
start_idx = (page - 1) * page_size
|
|
189
|
+
end_idx = start_idx + page_size
|
|
190
|
+
|
|
191
|
+
paginated_df = df.slice(start_idx, page_size)
|
|
192
|
+
|
|
193
|
+
metadata = {
|
|
194
|
+
"total": total,
|
|
195
|
+
"page": page,
|
|
196
|
+
"page_size": page_size,
|
|
197
|
+
"total_pages": total_pages,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return paginated_df, metadata
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def dataframe_to_dicts(df: pl.DataFrame) -> List[dict]:
|
|
204
|
+
"""
|
|
205
|
+
Convert Polars DataFrame to list of dictionaries with safe value conversion.
|
|
206
|
+
|
|
207
|
+
This function handles null values and Polars-specific types properly.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
df: Polars DataFrame to convert
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
List of dictionaries representing rows
|
|
214
|
+
|
|
215
|
+
Examples:
|
|
216
|
+
>>> df = pl.DataFrame({'a': [1, 2], 'b': ['x', 'y']})
|
|
217
|
+
>>> dataframe_to_dicts(df)
|
|
218
|
+
[{'a': 1, 'b': 'x'}, {'a': 2, 'b': 'y'}]
|
|
219
|
+
"""
|
|
220
|
+
return [{k: safe_get_value(v) for k, v in row.items()} for row in df.iter_rows(named=True)]
|
bidviz-1.0.0.dist-info/RECORD
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
bidviz/__init__.py,sha256=QybCA5HkOIZs1d_SdXTkvNlswsvVj10Zv4brKLrK9Tc,468
|
|
2
|
-
bidviz/exceptions.py,sha256=ASjWhnIEFUS2IypPSOz1i_Asfcs8V94uI7X-2LP8NII,2058
|
|
3
|
-
bidviz/transformer.py,sha256=ceet1EqWTpmwL9VI9iOl3wiICV0lkP3bYcpSvHL03ok,11046
|
|
4
|
-
bidviz/utils.py,sha256=h_tzdR7qMXzNhnA7bwPWXG1m4U8t3SmX17aevBFGrMo,4805
|
|
5
|
-
bidviz/core/__init__.py,sha256=SXXGnBf_H7Ld7zhs6CBtaWAu2PaEmi6TNSYWOx5LKD4,121
|
|
6
|
-
bidviz/core/base.py,sha256=PrcKjCb86ar0gfm5Mqw43w4zebRS2z_DFtoVo7sOnM0,1142
|
|
7
|
-
bidviz/transformers/__init__.py,sha256=z_bXeZAXfL7_kr6JSNFDDlSVA6KraiTiiqJAFQ32PMY,846
|
|
8
|
-
bidviz/transformers/bar.py,sha256=kjIbwPmgFmcGYuUV9_BOmuA9MNdlpac-5PrlkmrxhV8,2146
|
|
9
|
-
bidviz/transformers/heatmap.py,sha256=P-CP4lGLvqqXZZoDvNfr-bo4SXL1q2U-qfkW9rQG2gA,3951
|
|
10
|
-
bidviz/transformers/kpi.py,sha256=IJGLKgBCm1Cz9470yIrw2J6j6PA32KO7ICwN7qxNrK0,1786
|
|
11
|
-
bidviz/transformers/line.py,sha256=1wD9n0XSDlWEqzkWL-NidEKsjMQowgUslC22nySoHag,4119
|
|
12
|
-
bidviz/transformers/other.py,sha256=r_hPgW951b1aScDke6rmKzTSzvxZmGawDrWto8F_amw,3711
|
|
13
|
-
bidviz/transformers/pie.py,sha256=_fPg5Rmz2NQQUSQoV2AK0jrgW0Sgv14kQZPM41Sq_WY,1582
|
|
14
|
-
bidviz/transformers/table.py,sha256=JP-B-oTqvkQqNyY2n56GJJKuWJZTBnXJOpcwMetQ1uM,1580
|
|
15
|
-
bidviz-1.0.0.dist-info/licenses/LICENSE,sha256=gtUbjkVCe0nWrHAtj-adnWyYjITyH6Pi1jHY0oDib24,1075
|
|
16
|
-
bidviz-1.0.0.dist-info/METADATA,sha256=cAzyk8T9nE9kXYJjbvzMwqoo_R_ZU-940pEtFE0_sEA,11781
|
|
17
|
-
bidviz-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
-
bidviz-1.0.0.dist-info/top_level.txt,sha256=X935igwVnezMJZb1F9UCfmqGuj__FY34sWr8CgLWM3A,7
|
|
19
|
-
bidviz-1.0.0.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
bidviz
|