dukit 0.2.5__tar.gz → 0.2.7__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.5
3
+ Version: 0.2.7
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
@@ -89,8 +89,8 @@ these functions offer various ways to deal with non-unique keys/ids.
89
89
  primarily used when merging 2 dfs with a one-to-many relationship.
90
90
 
91
91
  available functions:
92
- - df.dk.flatten()
93
- - df.dk.stagger()
92
+ - df.dk.stack()
93
+ - df.dk.unstack()
94
94
  - df.dk.embed()
95
95
  - df.dk.collapse()
96
96
  <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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dukit"
3
- version = "0.2.5"
3
+ version = "0.2.7"
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" }]
@@ -24,16 +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
- get_dfs,
36
+ get_df_patients,
37
+ get_df_medications,
38
+ get_df_old,
39
+ get_df_new,
40
+ split_col,
41
+ embed,
42
+ collapse,
35
43
  stagger,
36
- transpose,
44
+ flatten,
37
45
  )
38
46
  from .os import (
39
47
  cd,
@@ -85,16 +93,24 @@ __all__ = (
85
93
  'format',
86
94
  'save',
87
95
 
88
- 'collapse',
96
+ 'compose',
97
+ 'decompose',
98
+ 'stack',
99
+ 'unstack',
100
+ 'transpose',
89
101
  'date_delta',
90
102
  'date_table',
91
103
  'deduplicate',
92
- 'embed',
93
- 'flatten',
94
104
  'get_df',
95
- 'get_dfs',
105
+ 'get_df_patients',
106
+ 'get_df_medications',
107
+ 'get_df_old',
108
+ 'get_df_new',
109
+ 'split_col',
110
+ 'embed',
111
+ 'collapse',
96
112
  'stagger',
97
- 'transpose',
113
+ 'flatten',
98
114
 
99
115
  'cd',
100
116
  'cp',
@@ -33,51 +33,6 @@ def _setup_xlsx(df_old, df_new, tmpdir):
33
33
 
34
34
 
35
35
 
36
- def _get_dfs():
37
-
38
- df_old = pd.DataFrame(
39
- columns=['a', 'b', 'c'],
40
- index=['x', 'y', 'z', ],
41
- )
42
-
43
- df_old.insert(0, 'uid', df_old.index)
44
-
45
- df_old.loc['x', 'a'] = 1
46
- df_old.loc['x', 'b'] = 1
47
- df_old.loc['x', 'c'] = 1
48
-
49
- df_old.loc['y', 'a'] = 2
50
- df_old.loc['y', 'b'] = 2
51
- df_old.loc['y', 'c'] = 2
52
-
53
- df_old.loc['z', 'a'] = 3
54
- df_old.loc['z', 'b'] = None
55
- df_old.loc['z', 'c'] = 3
56
-
57
-
58
- df_new = pd.DataFrame(
59
- columns=['d', 'b', 'a'],
60
- index=['y', 'x2', 'z', ],
61
- )
62
-
63
- df_new.insert(0, 'uid', df_new.index)
64
-
65
- df_new.loc['y', 'd'] = 2
66
- df_new.loc['y', 'b'] = 2
67
- df_new.loc['y', 'a'] = 0
68
-
69
- df_new.loc['x2', 'd'] = 1
70
- df_new.loc['x2', 'b'] = 1
71
- df_new.loc['x2', 'a'] = 1
72
-
73
- df_new.loc['z', 'd'] = 3
74
- df_new.loc['z', 'b'] = 3
75
- df_new.loc['z', 'a'] = pd.NA
76
-
77
- return df_old, df_new
78
-
79
-
80
-
81
36
  def _get_expected_new():
82
37
 
83
38
  uid = pd.Series(
@@ -86,7 +41,7 @@ def _get_expected_new():
86
41
  name='uid',
87
42
  )
88
43
  expected = pd.DataFrame(
89
- columns=['uid', 'diff', 'd', 'b', 'a'],
44
+ columns=['diff', 'uid', 'd', 'b', 'a'],
90
45
  index=uid,
91
46
  )
92
47
 
@@ -121,7 +76,7 @@ def _get_expected_newplus():
121
76
  name='uid',
122
77
  )
123
78
  expected = pd.DataFrame(
124
- columns=['uid', 'diff', 'd', 'b', 'b *old', 'a', 'a *old'],
79
+ columns=['diff', 'uid', 'd', 'b', 'b *old', 'a', 'a *old'],
125
80
  index=uid,
126
81
  )
127
82
 
@@ -165,7 +120,7 @@ def _get_expected_old():
165
120
  name='uid',
166
121
  )
167
122
  expected = pd.DataFrame(
168
- columns=['uid', 'diff', 'a', 'b', 'c'],
123
+ columns=['diff', 'uid', 'a', 'b', 'c'],
169
124
  index=uid,
170
125
  )
171
126
 
@@ -200,7 +155,7 @@ def _get_expected_mix():
200
155
  name='uid',
201
156
  )
202
157
  expected = pd.DataFrame(
203
- columns=['uid', 'diff', 'd', 'b', 'a', 'c'],
158
+ columns=['diff', 'uid', 'd', 'b', 'a', 'c'],
204
159
  index=uid,
205
160
  )
206
161
 
@@ -27,6 +27,7 @@ def diff(
27
27
  remove_cols: list | str = None,
28
28
  remove_cols_by_suffix='',
29
29
  retain_cols: list | str = None,
30
+ retain_rows: list | str = None,
30
31
  ignore_cols: list | str = None,
31
32
  remove_sheets: list | str = None,
32
33
  name='data',
@@ -50,11 +51,11 @@ def diff(
50
51
  controls the values included in each result.
51
52
  - 'mix': show rows and cols from old and new df.
52
53
  additions and deletions are highlighted.
53
- - 'old': show only rows and cols from the old df.
54
- deletions are highlighted.
55
54
  - 'new': show only rows and cols from the new df.
56
55
  additions are highlighted.
57
56
  - 'new+': also preserve changed old values in parallel metadata cols.
57
+ - 'old': show only rows and cols from the old df.
58
+ deletions are highlighted.
58
59
  rename_cols : dict, optional
59
60
  mapping of original col names to names used during comparison.
60
61
  remove_cols : list or str, optional
@@ -62,7 +63,9 @@ def diff(
62
63
  remove_cols_by_suffix : str, default ''
63
64
  remove cols whose names end with this suffix.
64
65
  retain_cols : list or str, optional
65
- keep only the cols from the old df.
66
+ keep these cols from the old df.
67
+ retain_rows : list or str, optional
68
+ keep these rows from the old df.
66
69
  ignore_cols : list or str, optional
67
70
  cols excluded from comparison.
68
71
  remove_sheets : list or str, optional
@@ -111,6 +114,7 @@ def diff(
111
114
  remove_cols=remove_cols,
112
115
  remove_cols_by_suffix=remove_cols_by_suffix,
113
116
  retain_cols=retain_cols,
117
+ retain_rows=retain_rows,
114
118
  ignore_cols=ignore_cols,
115
119
  remove_sheets=remove_sheets,
116
120
  name=name,
@@ -132,6 +136,7 @@ def rediff(
132
136
  remove_cols: list | str = 'diff',
133
137
  remove_cols_by_suffix=' *old',
134
138
  retain_cols: list | str = 'notes',
139
+ retain_rows: list | str = 'notes',
135
140
  ignore_cols: list | str = None,
136
141
  remove_sheets=['info', 'summary', 'details'],
137
142
  name='data',
@@ -154,6 +159,7 @@ def rediff(
154
159
  remove_cols=remove_cols,
155
160
  remove_cols_by_suffix=remove_cols_by_suffix,
156
161
  retain_cols=retain_cols,
162
+ retain_rows=retain_rows,
157
163
  ignore_cols=ignore_cols,
158
164
  remove_sheets=remove_sheets,
159
165
  name=name,
@@ -182,6 +188,7 @@ class Diffs:
182
188
  remove_cols: list | str = None,
183
189
  remove_cols_by_suffix='',
184
190
  retain_cols: list | str = None,
191
+ retain_rows: list | str = None,
185
192
  ignore_cols: list | str = None,
186
193
  remove_sheets: list | str = None,
187
194
  name='data',
@@ -232,6 +239,7 @@ class Diffs:
232
239
  remove_cols=remove_cols,
233
240
  remove_cols_by_suffix=remove_cols_by_suffix,
234
241
  retain_cols=retain_cols,
242
+ retain_rows=retain_rows,
235
243
  ignore_cols=ignore_cols,
236
244
  linebreak=linebreak,
237
245
  suffix_old=suffix_old,
@@ -641,6 +649,7 @@ def _get_diffs(
641
649
  remove_cols: list | str = None,
642
650
  remove_cols_by_suffix='',
643
651
  retain_cols: list | str = None,
652
+ retain_rows: list | str = None,
644
653
  ignore_cols: list | str = None,
645
654
  linebreak='<br>',
646
655
  suffix_old=' *old',
@@ -660,6 +669,7 @@ def _get_diffs(
660
669
  mode=mode,
661
670
  rename_cols=rename_cols,
662
671
  retain_cols=retain_cols,
672
+ retain_rows=retain_rows,
663
673
  remove_cols=remove_cols,
664
674
  remove_cols_by_suffix=remove_cols_by_suffix,
665
675
  ignore_cols=ignore_cols,
@@ -677,6 +687,7 @@ def _get_diffs(
677
687
  mode=mode,
678
688
  rename_cols=rename_cols,
679
689
  retain_cols=retain_cols,
690
+ retain_rows=retain_rows,
680
691
  remove_cols=remove_cols,
681
692
  remove_cols_by_suffix=remove_cols_by_suffix,
682
693
  ignore_cols=ignore_cols,
@@ -694,6 +705,7 @@ def _get_diffs(
694
705
  mode=mode,
695
706
  rename_cols=rename_cols,
696
707
  retain_cols=retain_cols,
708
+ retain_rows=retain_rows,
697
709
  remove_cols=remove_cols,
698
710
  remove_cols_by_suffix=remove_cols_by_suffix,
699
711
  ignore_cols=ignore_cols,
@@ -719,6 +731,7 @@ def _get_single_diff(
719
731
  remove_cols: list | str = None,
720
732
  remove_cols_by_suffix='',
721
733
  retain_cols: list | str = None,
734
+ retain_rows: list | str = None,
722
735
  ignore_cols: list | str = None,
723
736
  name='data',
724
737
  linebreak='<br>',
@@ -752,6 +765,7 @@ def _get_single_diff(
752
765
 
753
766
  d = _set_uid(d, uid)
754
767
  d = _retain_cols(d, list_(retain_cols)) #depends on _set_uid()
768
+ d = _retain_rows(d, list_(retain_rows)) #depends on _set_uid()
755
769
  d = _ignore_cols(d, list_(ignore_cols))
756
770
 
757
771
  d = _create_working_values(d)
@@ -769,8 +783,10 @@ def _get_single_diff(
769
783
  d = _populate_diff_col(d) #depends on _get_row_col_diffs() and _get_val_diffs()
770
784
 
771
785
  d = _add_retained_cols(d)
772
- d = _add_diff_col(d)
786
+ d = _add_retained_rows(d)
773
787
  d = _add_uid_col(d)
788
+ d = _add_diff_col(d)
789
+ d = _reset_index(d)
774
790
  d = _apply_style(d)
775
791
 
776
792
  return d
@@ -822,7 +838,8 @@ class Diff:
822
838
  self.vals_changed: pd.Series = pd.Series(dtype='int')
823
839
 
824
840
  #helper attributes for internal use
825
- self._df_retained: pd.DataFrame
841
+ self._cols_retained: pd.DataFrame
842
+ self._rows_retained: pd.DataFrame
826
843
  self._cols_ignore: list[str]
827
844
  self._metadata_col_mapping: dict[str, str]
828
845
  self._mask_added: pd.DataFrame
@@ -863,9 +880,10 @@ class Diff:
863
880
  f'vals changed: {sum(self.vals_changed)}\n'
864
881
  )
865
882
  txt += (
866
- '>>>d.result\n'
867
- '>>>d.summary\n'
868
- '>>>d.details\n'
883
+ '>>>d.show()\n'
884
+ '>>>d.info()\n'
885
+ '>>>d.summary()\n'
886
+ '>>>d.details()\n'
869
887
  )
870
888
  txt += '----------------Diff object end----------------\n'
871
889
 
@@ -919,7 +937,8 @@ def _handle_edgecases(d: Diff) -> Diff:
919
937
  d._values = values
920
938
  d._style = style
921
939
 
922
- d._df_retained = pd.DataFrame()
940
+ d._cols_retained = pd.DataFrame()
941
+ d._rows_retained = pd.DataFrame()
923
942
  d._cols_ignore = []
924
943
  d._metadata_col_mapping = {}
925
944
  d._mask_added = pd.DataFrame(dtype='bool')
@@ -1117,17 +1136,41 @@ def _retain_cols(d: Diff, cols: list) -> Diff:
1117
1136
  """
1118
1137
 
1119
1138
  if not cols:
1120
- d._df_retained = pd.DataFrame()
1139
+ d._cols_retained = pd.DataFrame()
1121
1140
  return d
1122
1141
 
1123
1142
  cols_retain_old = d.old.columns.intersection(cols)
1124
1143
  cols_retain_new = d.new.columns.intersection(cols)
1125
1144
 
1126
- df_retained = d.old[cols_retain_old].copy()
1145
+ cols_retained = d.old[cols_retain_old].copy()
1127
1146
 
1128
1147
  d.old = d.old.drop(columns=cols_retain_old)
1129
1148
  d.new = d.new.drop(columns=cols_retain_new)
1130
- d._df_retained = df_retained
1149
+ d._cols_retained = cols_retained
1150
+
1151
+ return d
1152
+
1153
+
1154
+
1155
+ def _retain_rows(d: Diff, rows: list) -> Diff:
1156
+ """
1157
+ remove rows from both dfs before diffing,
1158
+ then readd the ones from the old df to
1159
+ the diff result later.
1160
+ """
1161
+
1162
+ if not rows:
1163
+ d._rows_retained = pd.DataFrame()
1164
+ return d
1165
+
1166
+ rows_retain_old = d.old.index.intersection(rows)
1167
+ rows_retain_new = d.new.index.intersection(rows)
1168
+
1169
+ rows_retained = d.old.loc[rows_retain_old].copy()
1170
+
1171
+ d.old = d.old.drop(index=rows_retain_old)
1172
+ d.new = d.new.drop(index=rows_retain_new)
1173
+ d._rows_retained = rows_retained
1131
1174
 
1132
1175
  return d
1133
1176
 
@@ -1158,7 +1201,7 @@ def _create_working_values(d: Diff) -> Diff:
1158
1201
  if d.mode == 'mix':
1159
1202
  rows_old_only = d.old.index.difference(d.new.index)
1160
1203
  cols_old_only = d.old.columns.difference(d.new.columns)
1161
- d._values = pd.concat([d.new, d.old.loc[:, cols_old_only]], axis=1)
1204
+ d._values = pd.concat([d.new, d.old.loc[:, cols_old_only]], axis='columns')
1162
1205
  d._values.loc[rows_old_only, :] = d.old.loc[rows_old_only, :]
1163
1206
 
1164
1207
  elif d.mode == 'old':
@@ -1266,11 +1309,11 @@ def _create_meta_cols(d: Diff) -> Diff:
1266
1309
 
1267
1310
  values = pd.concat(
1268
1311
  [values, df_meta],
1269
- axis=1,
1312
+ axis='columns',
1270
1313
  )
1271
1314
  style = pd.concat(
1272
1315
  [style, df_meta_style],
1273
- axis=1,
1316
+ axis='columns',
1274
1317
  )
1275
1318
 
1276
1319
  d._metadata_col_mapping = col_mapping
@@ -1396,9 +1439,9 @@ def _populate_val_styles(d: Diff) -> Diff:
1396
1439
 
1397
1440
  def _populate_diff_col(d: Diff) -> Diff:
1398
1441
 
1399
- sum_added = d._mask_added.sum(axis=1)
1400
- sum_removed = d._mask_removed.sum(axis=1)
1401
- sum_changed = d._mask_changed.sum(axis=1)
1442
+ sum_added = d._mask_added.sum(axis='columns')
1443
+ sum_removed = d._mask_removed.sum(axis='columns')
1444
+ sum_changed = d._mask_changed.sum(axis='columns')
1402
1445
 
1403
1446
  rows_added = sum_added[sum_added > 0].index
1404
1447
  rows_removed = sum_removed[sum_removed > 0].index
@@ -1450,21 +1493,60 @@ def _populate_meta_cols(d: Diff) -> Diff:
1450
1493
 
1451
1494
  def _add_retained_cols(d: Diff) -> Diff:
1452
1495
 
1453
- if d._df_retained.empty:
1496
+ if d._cols_retained.empty:
1454
1497
  return d
1455
1498
 
1456
- idx_shared = d._values.index.intersection(d._df_retained.index)
1457
- df_retained = d._df_retained.loc[idx_shared, :]
1458
- d._values = pd.concat([df_retained, d._values], axis=1)
1499
+ idx_shared = d._values.index.intersection(d._cols_retained.index)
1500
+ df_retained = d._cols_retained.loc[idx_shared, :]
1501
+ d._values = pd.merge(
1502
+ df_retained,
1503
+ d._values,
1504
+ left_index=True,
1505
+ right_index=True,
1506
+ how='right',
1507
+ )
1459
1508
 
1509
+ df_style = pd.DataFrame(
1510
+ data='',
1511
+ index=df_retained.index,
1512
+ columns=df_retained.columns,
1513
+ )
1514
+ d._style = pd.merge(
1515
+ df_style,
1516
+ d._style,
1517
+ left_index=True,
1518
+ right_index=True,
1519
+ how='right',
1520
+ )
1460
1521
  return d
1461
1522
 
1462
1523
 
1463
1524
 
1464
- def _add_diff_col(d: Diff) -> Diff:
1465
- colname = ensure_unique_string('diff', d._values.columns)
1466
- d._diff_col.name = colname
1467
- d._values = pd.concat([d._diff_col, d._values], axis=1)
1525
+ def _add_retained_rows(d: Diff) -> Diff:
1526
+
1527
+ if d._rows_retained.empty:
1528
+ return d
1529
+
1530
+ cols_shared = d._values.columns.intersection(d._rows_retained.columns)
1531
+ df_retained = d._rows_retained.loc[:, cols_shared]
1532
+ cols_ordered = d._values.columns
1533
+ d._values = pd.concat([df_retained, d._values], axis='index')
1534
+ d._values = d._values[cols_ordered]
1535
+
1536
+ df_style = pd.DataFrame(
1537
+ data='',
1538
+ index=df_retained.index,
1539
+ columns=cols_shared,
1540
+ )
1541
+ d._style = pd.concat([df_style, d._style], axis='index')
1542
+
1543
+ blank_retained = pd.Series(
1544
+ '',
1545
+ index=df_retained.index,
1546
+ name=d._diff_col.name,
1547
+ )
1548
+ d._diff_col = pd.concat([blank_retained, d._diff_col], axis='index')
1549
+
1468
1550
  return d
1469
1551
 
1470
1552
 
@@ -1477,7 +1559,21 @@ def _add_uid_col(d: Diff) -> Diff:
1477
1559
  index=d._values.index,
1478
1560
  name=colname_uid,
1479
1561
  )
1480
- d._values = pd.concat([uid_col, d._values], axis=1)
1562
+ d._values = pd.concat([uid_col, d._values], axis='columns')
1563
+
1564
+ return d
1565
+
1566
+
1567
+
1568
+ def _add_diff_col(d: Diff) -> Diff:
1569
+ colname = ensure_unique_string('diff', d._values.columns)
1570
+ d._diff_col.name = colname
1571
+ d._values = pd.concat([d._diff_col, d._values], axis='columns')
1572
+ return d
1573
+
1574
+
1575
+
1576
+ def _reset_index(d: Diff) -> Diff:
1481
1577
 
1482
1578
  index_diff = d._values.index.difference(d._style.index)
1483
1579
  if index_diff.empty:
@@ -1489,7 +1585,7 @@ def _add_uid_col(d: Diff) -> Diff:
1489
1585
  'This should never happen. Please report this issue.'
1490
1586
  f'Index difference: {index_diff}'
1491
1587
  )
1492
- log(msg, 'dk.diffing._add_uid_col', d.verbosity)
1588
+ log(msg, 'dk.diffing._reset_index', d.verbosity)
1493
1589
 
1494
1590
  return d
1495
1591
 
@@ -1506,6 +1602,7 @@ def _apply_style(d: Diff) -> Diff:
1506
1602
  )
1507
1603
  log(msg, context, d.verbosity)
1508
1604
 
1605
+ d._style.fillna('', inplace=True)
1509
1606
  d.result = (
1510
1607
  d._values
1511
1608
  .style
@@ -136,6 +136,14 @@ def save(
136
136
  sheet_name='df',
137
137
  index=False,
138
138
  format_excel=True,
139
+ freeze_panes='B2',
140
+ hide_sheets=None,
141
+ hide_cols=None,
142
+ col_width_max=70,
143
+ col_width_padding=2,
144
+ align_vertical='top',
145
+ align_horizontal='left',
146
+ wrap_text=True,
139
147
  **kwargs,
140
148
  ):
141
149
  """
@@ -184,4 +192,15 @@ def save(
184
192
  )
185
193
 
186
194
  if format_excel:
187
- format(path, sheet=sheet_name)
195
+ format(
196
+ path,
197
+ sheet=sheet_name,
198
+ freeze_panes=freeze_panes,
199
+ hide_sheets=hide_sheets,
200
+ hide_cols=hide_cols,
201
+ col_width_max=col_width_max,
202
+ col_width_padding=col_width_padding,
203
+ align_vertical=align_vertical,
204
+ align_horizontal=align_horizontal,
205
+ wrap_text=wrap_text,
206
+ )