dukit 0.2.6__tar.gz → 0.3.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dukit
3
- Version: 0.2.6
3
+ Version: 0.3.0
4
4
  Summary: dukit - data utilities kit for exploring, modifying and visualizing small but messy data using pandas
5
5
  Keywords: pandas,query,utility,data,diff
6
6
  Author: MartinVoelkl94
@@ -10,7 +10,8 @@ Classifier: Programming Language :: Python
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Requires-Dist: ipython>=9.15.0
12
12
  Requires-Dist: jinja2>=3.1.6
13
- Requires-Dist: pandas>=3.0.3
13
+ Requires-Dist: openpyxl>=3.1.5
14
+ Requires-Dist: pandas>=3.1.0
14
15
  Requires-Python: >=3.14
15
16
  Project-URL: Repository, https://github.com/MartinVoelkl94/dukit
16
17
  Description-Content-Type: text/markdown
@@ -89,8 +90,8 @@ these functions offer various ways to deal with non-unique keys/ids.
89
90
  primarily used when merging 2 dfs with a one-to-many relationship.
90
91
 
91
92
  available functions:
92
- - df.dk.flatten()
93
- - df.dk.stagger()
93
+ - df.dk.stack()
94
+ - df.dk.unstack()
94
95
  - df.dk.embed()
95
96
  - df.dk.collapse()
96
97
  <br>
@@ -72,8 +72,8 @@ these functions offer various ways to deal with non-unique keys/ids.
72
72
  primarily used when merging 2 dfs with a one-to-many relationship.
73
73
 
74
74
  available functions:
75
- - df.dk.flatten()
76
- - df.dk.stagger()
75
+ - df.dk.stack()
76
+ - df.dk.unstack()
77
77
  - df.dk.embed()
78
78
  - df.dk.collapse()
79
79
  <br>
@@ -0,0 +1,48 @@
1
+ [project]
2
+ name = "dukit"
3
+ version = "0.3.0"
4
+ description = "dukit - data utilities kit for exploring, modifying and visualizing small but messy data using pandas"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.14"
8
+ classifiers = [
9
+ "Programming Language :: Python",
10
+ "Programming Language :: Python :: 3",
11
+ ]
12
+ keywords = [
13
+ "pandas",
14
+ "query",
15
+ "utility",
16
+ "data",
17
+ "diff",
18
+ ]
19
+ dependencies = [
20
+ "ipython>=9.15.0",
21
+ "jinja2>=3.1.6",
22
+ "openpyxl>=3.1.5",
23
+ "pandas>=3.1.0",
24
+ ]
25
+
26
+ [[project.authors]]
27
+ name = "MartinVoelkl94"
28
+ email = "martinv16248@gmail.com"
29
+
30
+ [project.urls]
31
+ Repository = "https://github.com/MartinVoelkl94/dukit"
32
+
33
+ [build-system]
34
+ requires = ["uv_build>=0.10.9,<0.11.0"]
35
+ build-backend = "uv_build"
36
+
37
+ [dependency-groups]
38
+ dev = [
39
+ "faker>=40.28.1",
40
+ "flake8>=7.3.0",
41
+ "ipykernel>=7.3.0",
42
+ "ipywidgets>=8.1.8",
43
+ "pip>=26.1.2",
44
+ "pylance>=8.0.0",
45
+ "pytest>=9.2.0",
46
+ "pytest-cov>=7.1.0",
47
+ "viztracer>=1.1.1",
48
+ ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dukit"
3
- version = "0.2.6"
3
+ version = "0.3.0"
4
4
  description = "dukit - data utilities kit for exploring, modifying and visualizing small but messy data using pandas"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "MartinVoelkl94", email = "martinv16248@gmail.com" }]
@@ -20,7 +20,8 @@ keywords = [
20
20
  dependencies = [
21
21
  "ipython>=9.15.0",
22
22
  "jinja2>=3.1.6",
23
- "pandas>=3.0.3",
23
+ "openpyxl>=3.1.5",
24
+ "pandas>=3.1.0",
24
25
  ]
25
26
 
26
27
 
@@ -39,10 +40,9 @@ dev = [
39
40
  "flake8>=7.3.0",
40
41
  "ipykernel>=7.3.0",
41
42
  "ipywidgets>=8.1.8",
42
- "openpyxl>=3.1.5",
43
43
  "pip>=26.1.2",
44
44
  "pylance>=8.0.0",
45
- "pytest>=9.1.1",
45
+ "pytest>=9.2.0",
46
46
  "pytest-cov>=7.1.0",
47
47
  "viztracer>=1.1.1",
48
48
  ]
@@ -24,20 +24,24 @@ from .excel import (
24
24
  save,
25
25
  )
26
26
  from .pandas import (
27
- collapse,
27
+ compose,
28
+ decompose,
29
+ stack,
30
+ unstack,
31
+ transpose,
28
32
  date_delta,
29
33
  date_table,
30
34
  deduplicate,
31
- embed,
32
- flatten,
33
35
  get_df,
34
36
  get_df_patients,
35
37
  get_df_medications,
36
38
  get_df_old,
37
39
  get_df_new,
38
- stagger,
39
- transpose,
40
40
  split_col,
41
+ embed,
42
+ collapse,
43
+ stagger,
44
+ flatten,
41
45
  )
42
46
  from .os import (
43
47
  cd,
@@ -89,20 +93,24 @@ __all__ = (
89
93
  'format',
90
94
  'save',
91
95
 
92
- 'collapse',
96
+ 'compose',
97
+ 'decompose',
98
+ 'stack',
99
+ 'unstack',
100
+ 'transpose',
93
101
  'date_delta',
94
102
  'date_table',
95
103
  'deduplicate',
96
- 'embed',
97
- 'flatten',
98
104
  'get_df',
99
105
  'get_df_patients',
100
106
  'get_df_medications',
101
107
  'get_df_old',
102
108
  'get_df_new',
103
- 'stagger',
104
- 'transpose',
105
109
  'split_col',
110
+ 'embed',
111
+ 'collapse',
112
+ 'stagger',
113
+ 'flatten',
106
114
 
107
115
  'cd',
108
116
  'cp',
@@ -63,7 +63,7 @@ def _get_expected_new():
63
63
  expected.loc['x2', 'a'] = 1
64
64
  expected.loc['z', 'a'] = pd.NA
65
65
 
66
- expected.reset_index(drop=True, inplace=True)
66
+ expected = expected.reset_index(drop=True)
67
67
 
68
68
  return expected.convert_dtypes()
69
69
 
@@ -107,7 +107,7 @@ def _get_expected_newplus():
107
107
  expected.loc['z', 'a *old'] = 3
108
108
 
109
109
  expected['b *old'] = expected['b *old'].astype('Int64')
110
- expected.reset_index(drop=True, inplace=True)
110
+ expected = expected.reset_index(drop=True)
111
111
 
112
112
  return expected.convert_dtypes()
113
113
 
@@ -142,7 +142,7 @@ def _get_expected_old():
142
142
  expected.loc['y', 'c'] = 2
143
143
  expected.loc['z', 'c'] = 3
144
144
 
145
- expected.reset_index(drop=True, inplace=True)
145
+ expected = expected.reset_index(drop=True)
146
146
 
147
147
  return expected.convert_dtypes()
148
148
 
@@ -187,7 +187,7 @@ def _get_expected_mix():
187
187
  expected.loc['z', 'c'] = 3
188
188
  expected.loc['x', 'c'] = 1
189
189
 
190
- expected.reset_index(drop=True, inplace=True)
190
+ expected = expected.reset_index(drop=True)
191
191
 
192
192
  return expected.convert_dtypes()
193
193
 
@@ -218,7 +218,7 @@ def _get_expected_style_new():
218
218
  expected.loc['x2', 'a'] = f'background-color: {GREEN};'
219
219
  expected.loc['z', 'a'] = f'background-color: {RED_LIGHT};'
220
220
 
221
- expected.reset_index(drop=True, inplace=True)
221
+ expected = expected.reset_index(drop=True)
222
222
 
223
223
  return expected.convert_dtypes()
224
224
 
@@ -256,7 +256,7 @@ def _get_expected_style_newplus():
256
256
  expected.loc['x2', 'a *old'] = f'font-style: italic;background-color: {GREEN};'
257
257
  expected.loc['z', 'a *old'] = 'font-style: italic;'
258
258
 
259
- expected.reset_index(drop=True, inplace=True)
259
+ expected = expected.reset_index(drop=True)
260
260
 
261
261
  return expected.convert_dtypes()
262
262
 
@@ -286,7 +286,7 @@ def _get_expected_style_old():
286
286
  expected.loc['y', 'c'] = f'background-color: {RED};'
287
287
  expected.loc['z', 'c'] = f'background-color: {RED};'
288
288
 
289
- expected.reset_index(drop=True, inplace=True)
289
+ expected = expected.reset_index(drop=True)
290
290
 
291
291
  return expected.convert_dtypes()
292
292
 
@@ -325,6 +325,6 @@ def _get_expected_style_mix():
325
325
  expected.loc['z', 'c'] = f'background-color: {RED};'
326
326
 
327
327
 
328
- expected.reset_index(drop=True, inplace=True)
328
+ expected = expected.reset_index(drop=True)
329
329
 
330
330
  return expected.convert_dtypes()
@@ -1058,15 +1058,15 @@ def _set_uid(d: Diff, uid: typing.Any) -> Diff:
1058
1058
  )
1059
1059
  d.old.index = d.old[uid]
1060
1060
  d.new.index = d.new[uid]
1061
- d.old.drop(columns=uid, inplace=True)
1062
- d.new.drop(columns=uid, inplace=True)
1061
+ d.old = d.old.drop(columns=uid)
1062
+ d.new = d.new.drop(columns=uid)
1063
1063
  d.uid = uid
1064
1064
 
1065
1065
  elif uid in d.old.columns and uid in d.new.columns:
1066
1066
  d.old.index = d.old[uid]
1067
1067
  d.new.index = d.new[uid]
1068
- d.old.drop(columns=uid, inplace=True)
1069
- d.new.drop(columns=uid, inplace=True)
1068
+ d.old = d.old.drop(columns=uid)
1069
+ d.new = d.new.drop(columns=uid)
1070
1070
  d.uid = uid
1071
1071
 
1072
1072
  else:
@@ -1295,7 +1295,7 @@ def _create_meta_cols(d: Diff) -> Diff:
1295
1295
 
1296
1296
  df_meta = values.loc[:, col_mapping.keys()].copy()
1297
1297
  df_meta.loc[:, :] = pd.NA
1298
- df_meta.rename(columns=col_mapping, inplace=True)
1298
+ df_meta = df_meta.rename(columns=col_mapping)
1299
1299
 
1300
1300
  data = {
1301
1301
  colname: ['font-style: italic;'] * len(values.index)
@@ -1498,15 +1498,26 @@ def _add_retained_cols(d: Diff) -> Diff:
1498
1498
 
1499
1499
  idx_shared = d._values.index.intersection(d._cols_retained.index)
1500
1500
  df_retained = d._cols_retained.loc[idx_shared, :]
1501
- d._values = pd.concat([df_retained, d._values], axis='columns')
1501
+ d._values = pd.merge(
1502
+ df_retained,
1503
+ d._values,
1504
+ left_index=True,
1505
+ right_index=True,
1506
+ how='right',
1507
+ )
1502
1508
 
1503
1509
  df_style = pd.DataFrame(
1504
1510
  data='',
1505
1511
  index=df_retained.index,
1506
1512
  columns=df_retained.columns,
1507
1513
  )
1508
- d._style = pd.concat([df_style, d._style], axis='columns')
1509
-
1514
+ d._style = pd.merge(
1515
+ df_style,
1516
+ d._style,
1517
+ left_index=True,
1518
+ right_index=True,
1519
+ how='right',
1520
+ )
1510
1521
  return d
1511
1522
 
1512
1523
 
@@ -1566,8 +1577,8 @@ def _reset_index(d: Diff) -> Diff:
1566
1577
 
1567
1578
  index_diff = d._values.index.difference(d._style.index)
1568
1579
  if index_diff.empty:
1569
- d._values.reset_index(drop=True, inplace=True)
1570
- d._style.reset_index(drop=True, inplace=True)
1580
+ d._values = d._values.reset_index(drop=True)
1581
+ d._style = d._style.reset_index(drop=True)
1571
1582
  else:
1572
1583
  msg = (
1573
1584
  'ERROR: index of values and style dfs do not match.'
@@ -1591,7 +1602,7 @@ def _apply_style(d: Diff) -> Diff:
1591
1602
  )
1592
1603
  log(msg, context, d.verbosity)
1593
1604
 
1594
- d._style.fillna('', inplace=True)
1605
+ d._style = d._style.fillna('')
1595
1606
  d.result = (
1596
1607
  d._values
1597
1608
  .style
@@ -9,13 +9,15 @@ from .qlang import (
9
9
  qs,
10
10
  )
11
11
  from .pandas import (
12
+ compose,
13
+ decompose,
14
+ stack,
15
+ unstack,
16
+ transpose,
12
17
  date_delta,
13
18
  date_table,
14
19
  collapse,
15
20
  embed,
16
- flatten,
17
- stagger,
18
- transpose,
19
21
  split_col,
20
22
  )
21
23
 
@@ -55,6 +57,124 @@ class DukitAccessor():
55
57
  return qr(self.df, code, verbosity)
56
58
 
57
59
 
60
+ def compose(
61
+ self,
62
+ col: str,
63
+ cols: list[str],
64
+ template='{0}_{1}',
65
+ verbosity=3,
66
+ ):
67
+ return compose(
68
+ self.df,
69
+ col=col,
70
+ cols=cols,
71
+ template=template,
72
+ verbosity=verbosity,
73
+ )
74
+
75
+
76
+ def stack(
77
+ self,
78
+ uid: str,
79
+ col_pattern: str = r'(?P<col>.+)_(?P<subid>.+)',
80
+ uid_template: str = '{uid}_{subid}',
81
+ cols_keep: list[str] | None = None,
82
+ dropna: bool = True,
83
+ sort: bool = True,
84
+ verbosity: int = 3,
85
+ ) -> pd.DataFrame:
86
+ df_new = stack(
87
+ self.df,
88
+ uid=uid,
89
+ col_pattern=col_pattern,
90
+ uid_template=uid_template,
91
+ cols_keep=cols_keep,
92
+ dropna=dropna,
93
+ sort=sort,
94
+ verbosity=verbosity,
95
+ )
96
+ return df_new
97
+
98
+
99
+ def unstack(
100
+ self,
101
+ uid: str,
102
+ uid_pattern: str = r'(?P<uid>.+)_(?P<subid>.+)',
103
+ col_template: str = '{col}_{subid}',
104
+ cols_keep: list[str] | None = None,
105
+ stagger: bool = True,
106
+ verbosity: int = 3,
107
+ ) -> pd.DataFrame:
108
+ df_new = unstack(
109
+ self.df,
110
+ uid=uid,
111
+ uid_pattern=uid_pattern,
112
+ col_template=col_template,
113
+ cols_keep=cols_keep,
114
+ stagger=stagger,
115
+ verbosity=verbosity
116
+ )
117
+ return df_new
118
+
119
+
120
+ def transpose(
121
+ self,
122
+ header='uid',
123
+ ) -> pd.DataFrame:
124
+ df_new = transpose(
125
+ self.df,
126
+ header=header,
127
+ )
128
+ return df_new
129
+
130
+
131
+ def save(
132
+ self,
133
+ path: str,
134
+ sheet_name='df',
135
+ index=False,
136
+ format_excel=True,
137
+ freeze_panes='B2',
138
+ hide_sheets=None,
139
+ hide_cols=None,
140
+ col_width_max=70,
141
+ col_width_padding=2,
142
+ align_vertical='top',
143
+ align_horizontal='left',
144
+ wrap_text=True,
145
+ **kwargs,
146
+ ):
147
+ save(
148
+ df=self.df,
149
+ path=path,
150
+ sheet_name=sheet_name,
151
+ index=index,
152
+ format_excel=format_excel,
153
+ freeze_panes=freeze_panes,
154
+ hide_sheets=hide_sheets,
155
+ hide_cols=hide_cols,
156
+ col_width_max=col_width_max,
157
+ col_width_padding=col_width_padding,
158
+ align_vertical=align_vertical,
159
+ align_horizontal=align_horizontal,
160
+ wrap_text=wrap_text,
161
+ **kwargs,
162
+ )
163
+ return None
164
+
165
+
166
+ def style(self):
167
+ code = r"""
168
+ .replace("\n", "<br>")
169
+ .wrap(normal)
170
+ .mono()
171
+ .align(left)
172
+ %.align(center)
173
+ """
174
+ df_styled = qs(self.df, code)
175
+ return df_styled
176
+
177
+
58
178
  def date_delta(
59
179
  self,
60
180
  reference_date: str | datetime.date | pd.Timestamp = None,
@@ -135,45 +255,6 @@ class DukitAccessor():
135
255
  return df_new
136
256
 
137
257
 
138
- def flatten(
139
- self,
140
- on: str,
141
- template='{colname}_#{counter}',
142
- ):
143
- df_new = flatten(
144
- self.df,
145
- on=on,
146
- template=template,
147
- )
148
- return df_new
149
-
150
-
151
- def stagger(
152
- self,
153
- on: str,
154
- template='#{counter}_{colname}',
155
- separator_col='#{counter}',
156
- ):
157
- df_new = stagger(
158
- self.df,
159
- on=on,
160
- template=template,
161
- separator_col=separator_col,
162
- )
163
- return df_new
164
-
165
-
166
- def transpose(
167
- self,
168
- header='uid',
169
- ) -> pd.DataFrame:
170
- df_new = transpose(
171
- self.df,
172
- header=header,
173
- )
174
- return df_new
175
-
176
-
177
258
  def split_col(
178
259
  self,
179
260
  col: str = 'uid',
@@ -189,50 +270,3 @@ class DukitAccessor():
189
270
  new2=new2,
190
271
  )
191
272
  return df_new
192
-
193
-
194
- def save(
195
- self,
196
- path: str,
197
- sheet_name='df',
198
- index=False,
199
- format_excel=True,
200
- freeze_panes='B2',
201
- hide_sheets=None,
202
- hide_cols=None,
203
- col_width_max=70,
204
- col_width_padding=2,
205
- align_vertical='top',
206
- align_horizontal='left',
207
- wrap_text=True,
208
- **kwargs,
209
- ):
210
- save(
211
- df=self.df,
212
- path=path,
213
- sheet_name=sheet_name,
214
- index=index,
215
- format_excel=format_excel,
216
- freeze_panes=freeze_panes,
217
- hide_sheets=hide_sheets,
218
- hide_cols=hide_cols,
219
- col_width_max=col_width_max,
220
- col_width_padding=col_width_padding,
221
- align_vertical=align_vertical,
222
- align_horizontal=align_horizontal,
223
- wrap_text=wrap_text,
224
- **kwargs,
225
- )
226
- return None
227
-
228
-
229
- def style(self):
230
- code = r"""
231
- .replace("\n", "<br>")
232
- .wrap(normal)
233
- .mono()
234
- .align(left)
235
- %.align(center)
236
- """
237
- df_styled = qs(self.df, code)
238
- return df_styled
@@ -101,7 +101,7 @@ def ls(path='', recursive=False) -> pd.DataFrame:
101
101
  )
102
102
  )
103
103
 
104
- files.drop(columns='_path', inplace=True)
104
+ files = files.drop(columns='_path')
105
105
 
106
106
  return files
107
107