dukit 0.2.5__tar.gz → 0.2.6__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.
- {dukit-0.2.5 → dukit-0.2.6}/PKG-INFO +1 -1
- {dukit-0.2.5 → dukit-0.2.6}/pyproject.toml +1 -1
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/__init__.py +10 -2
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/_test_utils.py +4 -49
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/diffing.py +114 -28
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/excel.py +20 -1
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/interfaces.py +40 -0
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/pandas.py +122 -18
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/qlang/symbols.py +180 -2
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/typing.py +9 -4
- {dukit-0.2.5 → dukit-0.2.6}/README.md +0 -0
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/os.py +0 -0
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/py.typed +0 -0
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/qlang/__init__.py +0 -0
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/qlang/engine.py +0 -0
- {dukit-0.2.5 → dukit-0.2.6}/src/dukit/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dukit"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.6"
|
|
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" }]
|
|
@@ -31,9 +31,13 @@ from .pandas import (
|
|
|
31
31
|
embed,
|
|
32
32
|
flatten,
|
|
33
33
|
get_df,
|
|
34
|
-
|
|
34
|
+
get_df_patients,
|
|
35
|
+
get_df_medications,
|
|
36
|
+
get_df_old,
|
|
37
|
+
get_df_new,
|
|
35
38
|
stagger,
|
|
36
39
|
transpose,
|
|
40
|
+
split_col,
|
|
37
41
|
)
|
|
38
42
|
from .os import (
|
|
39
43
|
cd,
|
|
@@ -92,9 +96,13 @@ __all__ = (
|
|
|
92
96
|
'embed',
|
|
93
97
|
'flatten',
|
|
94
98
|
'get_df',
|
|
95
|
-
'
|
|
99
|
+
'get_df_patients',
|
|
100
|
+
'get_df_medications',
|
|
101
|
+
'get_df_old',
|
|
102
|
+
'get_df_new',
|
|
96
103
|
'stagger',
|
|
97
104
|
'transpose',
|
|
105
|
+
'split_col',
|
|
98
106
|
|
|
99
107
|
'cd',
|
|
100
108
|
'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=['
|
|
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=['
|
|
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=['
|
|
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=['
|
|
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
|
|
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 =
|
|
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.
|
|
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.
|
|
867
|
-
'>>>d.
|
|
868
|
-
'>>>d.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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=
|
|
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=
|
|
1312
|
+
axis='columns',
|
|
1270
1313
|
)
|
|
1271
1314
|
style = pd.concat(
|
|
1272
1315
|
[style, df_meta_style],
|
|
1273
|
-
axis=
|
|
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=
|
|
1400
|
-
sum_removed = d._mask_removed.sum(axis=
|
|
1401
|
-
sum_changed = d._mask_changed.sum(axis=
|
|
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,49 @@ def _populate_meta_cols(d: Diff) -> Diff:
|
|
|
1450
1493
|
|
|
1451
1494
|
def _add_retained_cols(d: Diff) -> Diff:
|
|
1452
1495
|
|
|
1453
|
-
if d.
|
|
1496
|
+
if d._cols_retained.empty:
|
|
1454
1497
|
return d
|
|
1455
1498
|
|
|
1456
|
-
idx_shared = d._values.index.intersection(d.
|
|
1457
|
-
df_retained = d.
|
|
1458
|
-
d._values = pd.concat([df_retained, d._values], axis=
|
|
1499
|
+
idx_shared = d._values.index.intersection(d._cols_retained.index)
|
|
1500
|
+
df_retained = d._cols_retained.loc[idx_shared, :]
|
|
1501
|
+
d._values = pd.concat([df_retained, d._values], axis='columns')
|
|
1502
|
+
|
|
1503
|
+
df_style = pd.DataFrame(
|
|
1504
|
+
data='',
|
|
1505
|
+
index=df_retained.index,
|
|
1506
|
+
columns=df_retained.columns,
|
|
1507
|
+
)
|
|
1508
|
+
d._style = pd.concat([df_style, d._style], axis='columns')
|
|
1459
1509
|
|
|
1460
1510
|
return d
|
|
1461
1511
|
|
|
1462
1512
|
|
|
1463
1513
|
|
|
1464
|
-
def
|
|
1465
|
-
|
|
1466
|
-
d.
|
|
1467
|
-
|
|
1514
|
+
def _add_retained_rows(d: Diff) -> Diff:
|
|
1515
|
+
|
|
1516
|
+
if d._rows_retained.empty:
|
|
1517
|
+
return d
|
|
1518
|
+
|
|
1519
|
+
cols_shared = d._values.columns.intersection(d._rows_retained.columns)
|
|
1520
|
+
df_retained = d._rows_retained.loc[:, cols_shared]
|
|
1521
|
+
cols_ordered = d._values.columns
|
|
1522
|
+
d._values = pd.concat([df_retained, d._values], axis='index')
|
|
1523
|
+
d._values = d._values[cols_ordered]
|
|
1524
|
+
|
|
1525
|
+
df_style = pd.DataFrame(
|
|
1526
|
+
data='',
|
|
1527
|
+
index=df_retained.index,
|
|
1528
|
+
columns=cols_shared,
|
|
1529
|
+
)
|
|
1530
|
+
d._style = pd.concat([df_style, d._style], axis='index')
|
|
1531
|
+
|
|
1532
|
+
blank_retained = pd.Series(
|
|
1533
|
+
'',
|
|
1534
|
+
index=df_retained.index,
|
|
1535
|
+
name=d._diff_col.name,
|
|
1536
|
+
)
|
|
1537
|
+
d._diff_col = pd.concat([blank_retained, d._diff_col], axis='index')
|
|
1538
|
+
|
|
1468
1539
|
return d
|
|
1469
1540
|
|
|
1470
1541
|
|
|
@@ -1477,7 +1548,21 @@ def _add_uid_col(d: Diff) -> Diff:
|
|
|
1477
1548
|
index=d._values.index,
|
|
1478
1549
|
name=colname_uid,
|
|
1479
1550
|
)
|
|
1480
|
-
d._values = pd.concat([uid_col, d._values], axis=
|
|
1551
|
+
d._values = pd.concat([uid_col, d._values], axis='columns')
|
|
1552
|
+
|
|
1553
|
+
return d
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
|
|
1557
|
+
def _add_diff_col(d: Diff) -> Diff:
|
|
1558
|
+
colname = ensure_unique_string('diff', d._values.columns)
|
|
1559
|
+
d._diff_col.name = colname
|
|
1560
|
+
d._values = pd.concat([d._diff_col, d._values], axis='columns')
|
|
1561
|
+
return d
|
|
1562
|
+
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
def _reset_index(d: Diff) -> Diff:
|
|
1481
1566
|
|
|
1482
1567
|
index_diff = d._values.index.difference(d._style.index)
|
|
1483
1568
|
if index_diff.empty:
|
|
@@ -1489,7 +1574,7 @@ def _add_uid_col(d: Diff) -> Diff:
|
|
|
1489
1574
|
'This should never happen. Please report this issue.'
|
|
1490
1575
|
f'Index difference: {index_diff}'
|
|
1491
1576
|
)
|
|
1492
|
-
log(msg, 'dk.diffing.
|
|
1577
|
+
log(msg, 'dk.diffing._reset_index', d.verbosity)
|
|
1493
1578
|
|
|
1494
1579
|
return d
|
|
1495
1580
|
|
|
@@ -1506,6 +1591,7 @@ def _apply_style(d: Diff) -> Diff:
|
|
|
1506
1591
|
)
|
|
1507
1592
|
log(msg, context, d.verbosity)
|
|
1508
1593
|
|
|
1594
|
+
d._style.fillna('', inplace=True)
|
|
1509
1595
|
d.result = (
|
|
1510
1596
|
d._values
|
|
1511
1597
|
.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(
|
|
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
|
+
)
|
|
@@ -16,6 +16,7 @@ from .pandas import (
|
|
|
16
16
|
flatten,
|
|
17
17
|
stagger,
|
|
18
18
|
transpose,
|
|
19
|
+
split_col,
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
|
|
@@ -76,6 +77,9 @@ class DukitAccessor():
|
|
|
76
77
|
uid: str = None,
|
|
77
78
|
upper: int = None,
|
|
78
79
|
lower: int = None,
|
|
80
|
+
start_at_day1: bool = True,
|
|
81
|
+
schedule: dict[int, str] = None,
|
|
82
|
+
filler: str = '.',
|
|
79
83
|
linebreak: str = '<br>',
|
|
80
84
|
verbosity: int = 3,
|
|
81
85
|
):
|
|
@@ -85,6 +89,9 @@ class DukitAccessor():
|
|
|
85
89
|
uid=uid,
|
|
86
90
|
upper=upper,
|
|
87
91
|
lower=lower,
|
|
92
|
+
start_at_day1=start_at_day1,
|
|
93
|
+
schedule=schedule,
|
|
94
|
+
filler=filler,
|
|
88
95
|
linebreak=linebreak,
|
|
89
96
|
verbosity=verbosity,
|
|
90
97
|
)
|
|
@@ -167,12 +174,37 @@ class DukitAccessor():
|
|
|
167
174
|
return df_new
|
|
168
175
|
|
|
169
176
|
|
|
177
|
+
def split_col(
|
|
178
|
+
self,
|
|
179
|
+
col: str = 'uid',
|
|
180
|
+
sep: str = '::\n',
|
|
181
|
+
new1: str = None,
|
|
182
|
+
new2: str = 'label',
|
|
183
|
+
):
|
|
184
|
+
df_new = split_col(
|
|
185
|
+
self.df,
|
|
186
|
+
col=col,
|
|
187
|
+
sep=sep,
|
|
188
|
+
new1=new1,
|
|
189
|
+
new2=new2,
|
|
190
|
+
)
|
|
191
|
+
return df_new
|
|
192
|
+
|
|
193
|
+
|
|
170
194
|
def save(
|
|
171
195
|
self,
|
|
172
196
|
path: str,
|
|
173
197
|
sheet_name='df',
|
|
174
198
|
index=False,
|
|
175
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,
|
|
176
208
|
**kwargs,
|
|
177
209
|
):
|
|
178
210
|
save(
|
|
@@ -181,6 +213,14 @@ class DukitAccessor():
|
|
|
181
213
|
sheet_name=sheet_name,
|
|
182
214
|
index=index,
|
|
183
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,
|
|
184
224
|
**kwargs,
|
|
185
225
|
)
|
|
186
226
|
return None
|
|
@@ -4,7 +4,7 @@ import datetime
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
import pandas as pd
|
|
6
6
|
|
|
7
|
-
from .utils import log, GREEN_LIGHT
|
|
7
|
+
from .utils import log, ensure_unique_string, GREEN_LIGHT
|
|
8
8
|
from .typing import date_
|
|
9
9
|
|
|
10
10
|
|
|
@@ -183,8 +183,8 @@ def get_df() -> pd.DataFrame:
|
|
|
183
183
|
|
|
184
184
|
|
|
185
185
|
|
|
186
|
-
def
|
|
187
|
-
|
|
186
|
+
def get_df_patients() -> pd.DataFrame:
|
|
187
|
+
patients = pd.DataFrame({
|
|
188
188
|
'id': [
|
|
189
189
|
10001,
|
|
190
190
|
10002,
|
|
@@ -204,7 +204,12 @@ def get_dfs():
|
|
|
204
204
|
40,
|
|
205
205
|
],
|
|
206
206
|
})
|
|
207
|
-
|
|
207
|
+
return patients
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def get_df_medications() -> pd.DataFrame:
|
|
212
|
+
medications = pd.DataFrame({
|
|
208
213
|
'id': [
|
|
209
214
|
10002,
|
|
210
215
|
20001,
|
|
@@ -238,7 +243,55 @@ def get_dfs():
|
|
|
238
243
|
'mg',
|
|
239
244
|
]
|
|
240
245
|
})
|
|
241
|
-
return
|
|
246
|
+
return medications
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def get_df_old() -> pd.DataFrame:
|
|
250
|
+
|
|
251
|
+
old = pd.DataFrame(
|
|
252
|
+
columns=['a', 'b', 'c'],
|
|
253
|
+
index=['x', 'y', 'z', ],
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
old.insert(0, 'uid', old.index)
|
|
257
|
+
|
|
258
|
+
old.loc['x', 'a'] = 1
|
|
259
|
+
old.loc['x', 'b'] = 1
|
|
260
|
+
old.loc['x', 'c'] = 1
|
|
261
|
+
|
|
262
|
+
old.loc['y', 'a'] = 2
|
|
263
|
+
old.loc['y', 'b'] = 2
|
|
264
|
+
old.loc['y', 'c'] = 2
|
|
265
|
+
|
|
266
|
+
old.loc['z', 'a'] = 3
|
|
267
|
+
old.loc['z', 'b'] = None
|
|
268
|
+
old.loc['z', 'c'] = 3
|
|
269
|
+
|
|
270
|
+
return old
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def get_df_new() -> pd.DataFrame:
|
|
274
|
+
|
|
275
|
+
new = pd.DataFrame(
|
|
276
|
+
columns=['d', 'b', 'a'],
|
|
277
|
+
index=['y', 'x2', 'z', ],
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
new.insert(0, 'uid', new.index)
|
|
281
|
+
|
|
282
|
+
new.loc['y', 'd'] = 2
|
|
283
|
+
new.loc['y', 'b'] = 2
|
|
284
|
+
new.loc['y', 'a'] = 0
|
|
285
|
+
|
|
286
|
+
new.loc['x2', 'd'] = 1
|
|
287
|
+
new.loc['x2', 'b'] = 1
|
|
288
|
+
new.loc['x2', 'a'] = 1
|
|
289
|
+
|
|
290
|
+
new.loc['z', 'd'] = 3
|
|
291
|
+
new.loc['z', 'b'] = 3
|
|
292
|
+
new.loc['z', 'a'] = pd.NA
|
|
293
|
+
|
|
294
|
+
return new
|
|
242
295
|
|
|
243
296
|
|
|
244
297
|
|
|
@@ -342,6 +395,7 @@ def date_table(
|
|
|
342
395
|
lower: int = None,
|
|
343
396
|
start_at_day1: bool = True,
|
|
344
397
|
schedule: dict[int, str] = None,
|
|
398
|
+
filler: str = '.',
|
|
345
399
|
linebreak: str = '\n',
|
|
346
400
|
verbosity: int = 3,
|
|
347
401
|
) -> pd.DataFrame | pd.io.formats.style.Styler:
|
|
@@ -359,19 +413,17 @@ def date_table(
|
|
|
359
413
|
df = df.copy()
|
|
360
414
|
if uid:
|
|
361
415
|
df.index = df[uid]
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
df = (
|
|
365
|
-
df
|
|
366
|
-
.copy()
|
|
367
|
-
.map(date_)
|
|
368
|
-
.convert_dtypes()
|
|
369
|
-
)
|
|
416
|
+
df = df.map(date_).convert_dtypes()
|
|
370
417
|
|
|
371
418
|
for col in df.columns:
|
|
372
419
|
if df[col].isna().all():
|
|
373
|
-
|
|
420
|
+
if col == reference_col:
|
|
421
|
+
msg = f'WARNING: no dates found in reference col "{reference_col}"'
|
|
422
|
+
log(msg, 'dk.date_delta', verbosity)
|
|
423
|
+
else:
|
|
424
|
+
df.drop(columns=col, inplace=True)
|
|
374
425
|
|
|
426
|
+
reference = df[reference_col].apply(date_).apply(_fix_nas)
|
|
375
427
|
|
|
376
428
|
blanks = {col: pd.NaT for col in df.columns}
|
|
377
429
|
deltas = pd.DataFrame(
|
|
@@ -393,11 +445,10 @@ def date_table(
|
|
|
393
445
|
|
|
394
446
|
deltas = deltas.T
|
|
395
447
|
|
|
396
|
-
|
|
397
448
|
if upper is None:
|
|
398
|
-
upper = int(deltas.max().max()) + 1
|
|
449
|
+
upper = int(deltas.fillna(0).max().max()) + 1
|
|
399
450
|
if lower is None:
|
|
400
|
-
lower = int(deltas.min().min())
|
|
451
|
+
lower = int(deltas.fillna(0).min().min())
|
|
401
452
|
|
|
402
453
|
days = list(range(lower, upper + 1))
|
|
403
454
|
if start_at_day1:
|
|
@@ -427,7 +478,7 @@ def date_table(
|
|
|
427
478
|
if start_at_day1:
|
|
428
479
|
events = _shift_day0(events)
|
|
429
480
|
|
|
430
|
-
merged = days_df.copy().merge(events, on='days', how='left').fillna(
|
|
481
|
+
merged = days_df.copy().merge(events, on='days', how='left').fillna(filler)
|
|
431
482
|
collapsed = collapse(
|
|
432
483
|
merged,
|
|
433
484
|
on='days',
|
|
@@ -439,6 +490,23 @@ def date_table(
|
|
|
439
490
|
how='left',
|
|
440
491
|
)
|
|
441
492
|
|
|
493
|
+
cols_records = [
|
|
494
|
+
col for col
|
|
495
|
+
in df_timeline.columns
|
|
496
|
+
if col not in ('_meta', 'days', 'planned')
|
|
497
|
+
]
|
|
498
|
+
df_timeline.insert(
|
|
499
|
+
loc=0,
|
|
500
|
+
column='_meta',
|
|
501
|
+
value=''
|
|
502
|
+
)
|
|
503
|
+
has_event = df_timeline[cols_records] != filler
|
|
504
|
+
records_per_row = has_event.sum(axis='columns')
|
|
505
|
+
rows_with_records = records_per_row > 0
|
|
506
|
+
summary = 'records: ' + records_per_row.astype(str)
|
|
507
|
+
df_timeline.loc[rows_with_records, '_meta'] = summary
|
|
508
|
+
df_timeline['_meta'] = df_timeline['_meta'].fillna('')
|
|
509
|
+
|
|
442
510
|
if schedule is not None:
|
|
443
511
|
df_timeline = _highlight_schedule(df_timeline, schedule)
|
|
444
512
|
|
|
@@ -819,3 +887,39 @@ def transpose(
|
|
|
819
887
|
df.reset_index(drop=True, inplace=True)
|
|
820
888
|
|
|
821
889
|
return df
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
def split_col(
|
|
894
|
+
df: pd.DataFrame,
|
|
895
|
+
col: str = 'uid',
|
|
896
|
+
sep: str = '::\n',
|
|
897
|
+
new1: str = None,
|
|
898
|
+
new2: str = 'label',
|
|
899
|
+
):
|
|
900
|
+
|
|
901
|
+
if col not in df.columns:
|
|
902
|
+
log(f'ERROR: no col named "{col}" found')
|
|
903
|
+
return df
|
|
904
|
+
elif df.columns.tolist().count(col) > 1:
|
|
905
|
+
log(f'ERROR: multiple cols named "{col}" found')
|
|
906
|
+
return df
|
|
907
|
+
|
|
908
|
+
df = df.copy()
|
|
909
|
+
pos = df.columns.get_loc(col)
|
|
910
|
+
splits = df[col].str.partition(sep)
|
|
911
|
+
|
|
912
|
+
df.drop(columns=[col], inplace=True)
|
|
913
|
+
if new1 is None:
|
|
914
|
+
new1 = col
|
|
915
|
+
elif new1 in df.columns:
|
|
916
|
+
log(f'WARNING: Col {new1} already exists, choosing new name.')
|
|
917
|
+
new1 = ensure_unique_string(new1, df.columns.tolist())
|
|
918
|
+
if new2 in df.columns:
|
|
919
|
+
log(f'WARNING: Col {new2} already exists, choosing new name.')
|
|
920
|
+
new2 = ensure_unique_string(new2, df.columns.tolist() + [new1])
|
|
921
|
+
|
|
922
|
+
df.insert(pos, new1, splits[0]) #type: ignore
|
|
923
|
+
df.insert(pos + 1, new2, splits[2]) #type: ignore
|
|
924
|
+
|
|
925
|
+
return df
|
|
@@ -1413,8 +1413,8 @@ class GetNotEquals(Symbol):
|
|
|
1413
1413
|
|
|
1414
1414
|
class GetContains(Symbol):
|
|
1415
1415
|
"""
|
|
1416
|
-
get cols/rows/vals if
|
|
1417
|
-
|
|
1416
|
+
get cols/rows/vals if they
|
|
1417
|
+
contain a given string.
|
|
1418
1418
|
|
|
1419
1419
|
Examples
|
|
1420
1420
|
--------
|
|
@@ -1495,6 +1495,184 @@ class GetContains(Symbol):
|
|
|
1495
1495
|
|
|
1496
1496
|
|
|
1497
1497
|
|
|
1498
|
+
class GetStartsWith(Symbol):
|
|
1499
|
+
"""
|
|
1500
|
+
get cols/rows/vals if they
|
|
1501
|
+
start with a given string.
|
|
1502
|
+
|
|
1503
|
+
Examples
|
|
1504
|
+
--------
|
|
1505
|
+
>>> df.dk.qs(r'name :start(john)')
|
|
1506
|
+
"""
|
|
1507
|
+
|
|
1508
|
+
#symbol attributes
|
|
1509
|
+
name = 'GetStartsWith'
|
|
1510
|
+
category = 'getter'
|
|
1511
|
+
regex = (
|
|
1512
|
+
r':startswith',
|
|
1513
|
+
r':starts',
|
|
1514
|
+
r':start',
|
|
1515
|
+
)
|
|
1516
|
+
|
|
1517
|
+
#used to validate the current op
|
|
1518
|
+
connectors_allowed = {
|
|
1519
|
+
'new': 'start a new op',
|
|
1520
|
+
'and': 'combine getter selection with current selection using logical AND',
|
|
1521
|
+
'or': 'combine getter selection with current selection using logical OR',
|
|
1522
|
+
}
|
|
1523
|
+
scopes_allowed = {
|
|
1524
|
+
'cols': 'get or set cols/headers',
|
|
1525
|
+
'rows': 'get or set rows/index',
|
|
1526
|
+
'vals': 'get or set vals within current row and col selection',
|
|
1527
|
+
}
|
|
1528
|
+
flags_allowed = {
|
|
1529
|
+
'negate': 'negate the condition',
|
|
1530
|
+
'regex': 'parse arg as regex',
|
|
1531
|
+
|
|
1532
|
+
'index': 'condition is applied to index instead of vals',
|
|
1533
|
+
'any': 'condition must apply to any args',
|
|
1534
|
+
'all': 'condition must apply to all args',
|
|
1535
|
+
'allcols': 'get rows where condition applies in all selected cols',
|
|
1536
|
+
|
|
1537
|
+
'strict': 'strict type comparison/conversion',
|
|
1538
|
+
}
|
|
1539
|
+
args_allowed = {}
|
|
1540
|
+
args_min = 1
|
|
1541
|
+
args_max = 1_000_000
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
def parse(self, q: Query) -> Query:
|
|
1545
|
+
q = _preparse_for_getter(q)
|
|
1546
|
+
q = _parse_op_symbol(self, q)
|
|
1547
|
+
return q
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
def run(self, q: Query) -> Query:
|
|
1551
|
+
if self.scope == 'cols':
|
|
1552
|
+
q = _get_cols(self, q)
|
|
1553
|
+
elif self.scope == 'rows':
|
|
1554
|
+
q = _get_rows(self, q)
|
|
1555
|
+
elif self.scope == 'vals':
|
|
1556
|
+
q = _get_vals(self, q)
|
|
1557
|
+
return q
|
|
1558
|
+
|
|
1559
|
+
|
|
1560
|
+
def getter(
|
|
1561
|
+
self,
|
|
1562
|
+
series: pd.Series,
|
|
1563
|
+
mask: pd.Series[bool],
|
|
1564
|
+
arg: str,
|
|
1565
|
+
q: Query,
|
|
1566
|
+
) -> pd.Series[bool]:
|
|
1567
|
+
|
|
1568
|
+
series = series.astype('string')
|
|
1569
|
+
|
|
1570
|
+
if 'regex' in self.flags:
|
|
1571
|
+
mask = series.str.match(arg)
|
|
1572
|
+
|
|
1573
|
+
elif 'strict' in self.flags:
|
|
1574
|
+
mask = series.str.startswith(arg)
|
|
1575
|
+
|
|
1576
|
+
else:
|
|
1577
|
+
series = series.str.lower()
|
|
1578
|
+
arg = arg.lower()
|
|
1579
|
+
mask = series.str.startswith(arg)
|
|
1580
|
+
|
|
1581
|
+
return mask
|
|
1582
|
+
|
|
1583
|
+
|
|
1584
|
+
|
|
1585
|
+
|
|
1586
|
+
class GetEndsWith(Symbol):
|
|
1587
|
+
"""
|
|
1588
|
+
get cols/rows/vals if they
|
|
1589
|
+
end with a given string.
|
|
1590
|
+
|
|
1591
|
+
Examples
|
|
1592
|
+
--------
|
|
1593
|
+
>>> df.dk.qs(r'name :end(doe)')
|
|
1594
|
+
"""
|
|
1595
|
+
|
|
1596
|
+
#symbol attributes
|
|
1597
|
+
name = 'GetEndsWith'
|
|
1598
|
+
category = 'getter'
|
|
1599
|
+
regex = (
|
|
1600
|
+
r':endswith',
|
|
1601
|
+
r':ends',
|
|
1602
|
+
r':end',
|
|
1603
|
+
)
|
|
1604
|
+
|
|
1605
|
+
#used to validate the current op
|
|
1606
|
+
connectors_allowed = {
|
|
1607
|
+
'new': 'start a new op',
|
|
1608
|
+
'and': 'combine getter selection with current selection using logical AND',
|
|
1609
|
+
'or': 'combine getter selection with current selection using logical OR',
|
|
1610
|
+
}
|
|
1611
|
+
scopes_allowed = {
|
|
1612
|
+
'cols': 'get or set cols/headers',
|
|
1613
|
+
'rows': 'get or set rows/index',
|
|
1614
|
+
'vals': 'get or set vals within current row and col selection',
|
|
1615
|
+
}
|
|
1616
|
+
flags_allowed = {
|
|
1617
|
+
'negate': 'negate the condition',
|
|
1618
|
+
'regex': 'parse arg as regex',
|
|
1619
|
+
|
|
1620
|
+
'index': 'condition is applied to index instead of vals',
|
|
1621
|
+
'any': 'condition must apply to any args',
|
|
1622
|
+
'all': 'condition must apply to all args',
|
|
1623
|
+
'allcols': 'get rows where condition applies in all selected cols',
|
|
1624
|
+
|
|
1625
|
+
'strict': 'strict type comparison/conversion',
|
|
1626
|
+
}
|
|
1627
|
+
args_allowed = {}
|
|
1628
|
+
args_min = 1
|
|
1629
|
+
args_max = 1_000_000
|
|
1630
|
+
|
|
1631
|
+
|
|
1632
|
+
def parse(self, q: Query) -> Query:
|
|
1633
|
+
q = _preparse_for_getter(q)
|
|
1634
|
+
q = _parse_op_symbol(self, q)
|
|
1635
|
+
return q
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
def run(self, q: Query) -> Query:
|
|
1639
|
+
if self.scope == 'cols':
|
|
1640
|
+
q = _get_cols(self, q)
|
|
1641
|
+
elif self.scope == 'rows':
|
|
1642
|
+
q = _get_rows(self, q)
|
|
1643
|
+
elif self.scope == 'vals':
|
|
1644
|
+
q = _get_vals(self, q)
|
|
1645
|
+
return q
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
def getter(
|
|
1649
|
+
self,
|
|
1650
|
+
series: pd.Series,
|
|
1651
|
+
mask: pd.Series[bool],
|
|
1652
|
+
arg: str,
|
|
1653
|
+
q: Query,
|
|
1654
|
+
) -> pd.Series[bool]:
|
|
1655
|
+
|
|
1656
|
+
series = series.astype('string')
|
|
1657
|
+
|
|
1658
|
+
if 'regex' in self.flags:
|
|
1659
|
+
if not arg.endswith('$'):
|
|
1660
|
+
arg = arg + '$'
|
|
1661
|
+
mask = series.str.contains(arg)
|
|
1662
|
+
|
|
1663
|
+
elif 'strict' in self.flags:
|
|
1664
|
+
mask = series.str.endswith(arg)
|
|
1665
|
+
|
|
1666
|
+
else:
|
|
1667
|
+
series = series.str.lower()
|
|
1668
|
+
arg = arg.lower()
|
|
1669
|
+
mask = series.str.endswith(arg)
|
|
1670
|
+
|
|
1671
|
+
return mask
|
|
1672
|
+
|
|
1673
|
+
|
|
1674
|
+
|
|
1675
|
+
|
|
1498
1676
|
class GetGreaterEqual(Symbol):
|
|
1499
1677
|
"""
|
|
1500
1678
|
get cols/rows/vals if
|
|
@@ -84,6 +84,10 @@ VALUES_NA = (
|
|
|
84
84
|
'blank',
|
|
85
85
|
'empty',
|
|
86
86
|
)
|
|
87
|
+
VALUES_NA_STRICT = (
|
|
88
|
+
'<na>',
|
|
89
|
+
'none',
|
|
90
|
+
)
|
|
87
91
|
VALUES_NK = (
|
|
88
92
|
'unk',
|
|
89
93
|
'unknown',
|
|
@@ -487,6 +491,10 @@ def type_(x) -> str:
|
|
|
487
491
|
return 'int'
|
|
488
492
|
elif isinstance(x, TYPES_FLOAT):
|
|
489
493
|
return 'float'
|
|
494
|
+
elif pd.isna(x):
|
|
495
|
+
return 'na'
|
|
496
|
+
elif x == '':
|
|
497
|
+
return 'str'
|
|
490
498
|
elif datetime_(x) is not pd.NaT:
|
|
491
499
|
x = str(x).strip()
|
|
492
500
|
if re.fullmatch(r'\d*', x):
|
|
@@ -504,7 +512,7 @@ def type_(x) -> str:
|
|
|
504
512
|
return 'int'
|
|
505
513
|
elif re.fullmatch(r'-?\d+[\.,]\d+', x):
|
|
506
514
|
return 'float'
|
|
507
|
-
elif str(x).lower() in
|
|
515
|
+
elif str(x).lower() in VALUES_NA_STRICT:
|
|
508
516
|
return 'na'
|
|
509
517
|
else:
|
|
510
518
|
try:
|
|
@@ -513,9 +521,6 @@ def type_(x) -> str:
|
|
|
513
521
|
except Exception:
|
|
514
522
|
return 'str'
|
|
515
523
|
|
|
516
|
-
elif pd.isna(x):
|
|
517
|
-
return 'na'
|
|
518
|
-
|
|
519
524
|
else:
|
|
520
525
|
return type(x).__name__
|
|
521
526
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|