dukit 0.2.2__tar.gz → 0.2.4__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.2 → dukit-0.2.4}/PKG-INFO +1 -1
- {dukit-0.2.2 → dukit-0.2.4}/pyproject.toml +1 -1
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/__init__.py +2 -0
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/diffing.py +0 -4
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/excel.py +3 -6
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/interfaces.py +45 -6
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/os.py +1 -1
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/pandas.py +191 -31
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/qlang/symbols.py +57 -6
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/typing.py +2 -2
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/utils.py +1 -1
- {dukit-0.2.2 → dukit-0.2.4}/README.md +0 -0
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/_test_utils.py +0 -0
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/py.typed +0 -0
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/qlang/__init__.py +0 -0
- {dukit-0.2.2 → dukit-0.2.4}/src/dukit/qlang/engine.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dukit"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.4"
|
|
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" }]
|
|
@@ -25,6 +25,7 @@ from .pandas import (
|
|
|
25
25
|
collapse,
|
|
26
26
|
transpose,
|
|
27
27
|
date_delta,
|
|
28
|
+
date_table,
|
|
28
29
|
)
|
|
29
30
|
from .excel import (
|
|
30
31
|
format,
|
|
@@ -78,6 +79,7 @@ __all__ = (
|
|
|
78
79
|
'collapse',
|
|
79
80
|
'transpose',
|
|
80
81
|
'date_delta',
|
|
82
|
+
'date_table',
|
|
81
83
|
'format',
|
|
82
84
|
'save',
|
|
83
85
|
'Box',
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
|
|
2
2
|
import pandas as pd
|
|
3
|
-
import openpyxl
|
|
4
3
|
import typing
|
|
5
4
|
import os
|
|
6
5
|
|
|
@@ -447,7 +446,6 @@ class Diffs:
|
|
|
447
446
|
|
|
448
447
|
#format excel file
|
|
449
448
|
if apply_format:
|
|
450
|
-
wb = openpyxl.load_workbook(path)
|
|
451
449
|
|
|
452
450
|
#metadata sheets
|
|
453
451
|
sheets_hide = []
|
|
@@ -462,7 +460,6 @@ class Diffs:
|
|
|
462
460
|
sheet=[sheet_info, sheet_summary, sheet_details],
|
|
463
461
|
hide_sheets=sheets_hide,
|
|
464
462
|
freeze_panes='B2',
|
|
465
|
-
openpyxl_workbook=wb,
|
|
466
463
|
)
|
|
467
464
|
msg = 'DEBUG: info, summary, details sheets formatted'
|
|
468
465
|
log(msg, context, self.verbosity)
|
|
@@ -478,7 +475,6 @@ class Diffs:
|
|
|
478
475
|
sheet=diff.name,
|
|
479
476
|
hide_cols=cols_hide,
|
|
480
477
|
freeze_panes=freeze_panes,
|
|
481
|
-
openpyxl_workbook=wb,
|
|
482
478
|
)
|
|
483
479
|
msg = f'DEBUG: diff sheet formatted: "{diff.name}"'
|
|
484
480
|
log(msg, context, self.verbosity)
|
|
@@ -19,9 +19,8 @@ def format(
|
|
|
19
19
|
align_vertical='top',
|
|
20
20
|
align_horizontal='left',
|
|
21
21
|
wrap_text=True,
|
|
22
|
-
openpyxl_workbook=None,
|
|
23
22
|
verbosity=3,
|
|
24
|
-
):
|
|
23
|
+
):
|
|
25
24
|
"""
|
|
26
25
|
applies formatting to an Excel file:
|
|
27
26
|
- adjust col width to max length of cell content (accounts for linebreaks)
|
|
@@ -29,10 +28,8 @@ def format(
|
|
|
29
28
|
- hide specified columns
|
|
30
29
|
"""
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
else:
|
|
35
|
-
wb = openpyxl.load_workbook(path)
|
|
31
|
+
wb = openpyxl.load_workbook(path)
|
|
32
|
+
|
|
36
33
|
if sheet:
|
|
37
34
|
sheetnames = list_(sheet)
|
|
38
35
|
else:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import pandas as pd
|
|
3
|
+
import datetime
|
|
3
4
|
from .excel import save
|
|
4
5
|
from .qlang import (
|
|
5
6
|
q,
|
|
@@ -12,6 +13,8 @@ from .pandas import (
|
|
|
12
13
|
embed,
|
|
13
14
|
collapse,
|
|
14
15
|
transpose,
|
|
16
|
+
date_delta,
|
|
17
|
+
date_table,
|
|
15
18
|
)
|
|
16
19
|
|
|
17
20
|
|
|
@@ -104,16 +107,14 @@ class DukitAccessor():
|
|
|
104
107
|
def collapse(
|
|
105
108
|
self,
|
|
106
109
|
on: str,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
line_stop='\n',
|
|
110
|
+
template_col='{colname}',
|
|
111
|
+
template_item='#{counter}: {item}\n',
|
|
110
112
|
):
|
|
111
113
|
df_new = collapse(
|
|
112
114
|
self.df,
|
|
113
115
|
on=on,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
line_stop=line_stop,
|
|
116
|
+
template_col=template_col,
|
|
117
|
+
template_item=template_item,
|
|
117
118
|
)
|
|
118
119
|
return df_new
|
|
119
120
|
|
|
@@ -129,6 +130,44 @@ class DukitAccessor():
|
|
|
129
130
|
return df_new
|
|
130
131
|
|
|
131
132
|
|
|
133
|
+
def date_delta(
|
|
134
|
+
self,
|
|
135
|
+
reference_date: str | datetime.date | pd.Timestamp = None,
|
|
136
|
+
reference_col: str = None,
|
|
137
|
+
linebreak: str = '<br>',
|
|
138
|
+
verbosity: int = 3,
|
|
139
|
+
):
|
|
140
|
+
return date_delta(
|
|
141
|
+
self.df,
|
|
142
|
+
reference_date=reference_date,
|
|
143
|
+
reference_col=reference_col,
|
|
144
|
+
linebreak=linebreak,
|
|
145
|
+
verbosity=verbosity,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def date_table(
|
|
150
|
+
self,
|
|
151
|
+
reference_date: str | datetime.date | pd.Timestamp = None,
|
|
152
|
+
reference_col: str = None,
|
|
153
|
+
uid: str = None,
|
|
154
|
+
upper: int = None,
|
|
155
|
+
lower: int = None,
|
|
156
|
+
linebreak: str = '<br>',
|
|
157
|
+
verbosity: int = 3,
|
|
158
|
+
):
|
|
159
|
+
return date_table(
|
|
160
|
+
df=self.df,
|
|
161
|
+
reference_date=reference_date,
|
|
162
|
+
reference_col=reference_col,
|
|
163
|
+
uid=uid,
|
|
164
|
+
upper=upper,
|
|
165
|
+
lower=lower,
|
|
166
|
+
linebreak=linebreak,
|
|
167
|
+
verbosity=verbosity,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
132
171
|
def save(
|
|
133
172
|
self,
|
|
134
173
|
path: str,
|
|
@@ -262,7 +262,7 @@ def fetch(path, before='now', verbosity=3):
|
|
|
262
262
|
timestamp = datetime_(timestamp_str)
|
|
263
263
|
if timestamp < datetime_(cutoff):
|
|
264
264
|
timestamps[timestamp] = (timestamp_str, extension)
|
|
265
|
-
except Exception:
|
|
265
|
+
except Exception:
|
|
266
266
|
pass
|
|
267
267
|
if len(timestamps) == 0:
|
|
268
268
|
text = (
|
|
@@ -3,7 +3,7 @@ import pandas as pd
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import datetime
|
|
5
5
|
import copy
|
|
6
|
-
from .utils import log
|
|
6
|
+
from .utils import log, GREEN_LIGHT
|
|
7
7
|
from .typing import date_
|
|
8
8
|
|
|
9
9
|
def get_df() -> pd.DataFrame:
|
|
@@ -446,13 +446,12 @@ def embed(
|
|
|
446
446
|
def collapse(
|
|
447
447
|
df: pd.DataFrame,
|
|
448
448
|
on: str,
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
line_stop='\n',
|
|
449
|
+
template_col='{colname}',
|
|
450
|
+
template_item='#{counter}: {item}\n',
|
|
452
451
|
):
|
|
453
452
|
df = df.groupby(on).agg(list)
|
|
454
|
-
df = df.map(lambda x: _to_lines(x,
|
|
455
|
-
df.columns = [
|
|
453
|
+
df = df.map(lambda x: _to_lines(x, template_item))
|
|
454
|
+
df.columns = [template_col.format(colname=col) for col in df.columns]
|
|
456
455
|
df.insert(0, on, df.index)
|
|
457
456
|
df.reset_index(drop=True, inplace=True)
|
|
458
457
|
return df
|
|
@@ -460,8 +459,7 @@ def collapse(
|
|
|
460
459
|
|
|
461
460
|
def _to_lines(
|
|
462
461
|
x,
|
|
463
|
-
|
|
464
|
-
line_stop='\n',
|
|
462
|
+
template_item='#{counter}: {item}\n',
|
|
465
463
|
):
|
|
466
464
|
if not isinstance(x, list):
|
|
467
465
|
return x
|
|
@@ -470,7 +468,7 @@ def _to_lines(
|
|
|
470
468
|
else:
|
|
471
469
|
x_str = ''
|
|
472
470
|
for i, item in enumerate(x):
|
|
473
|
-
x_str +=
|
|
471
|
+
x_str += template_item.format(counter=i + 1, item=item)
|
|
474
472
|
return x_str
|
|
475
473
|
|
|
476
474
|
|
|
@@ -494,24 +492,18 @@ def transpose(
|
|
|
494
492
|
|
|
495
493
|
|
|
496
494
|
def date_delta(
|
|
497
|
-
df,
|
|
498
|
-
reference_date=None,
|
|
499
|
-
reference_col=None,
|
|
500
|
-
|
|
495
|
+
df: pd.DataFrame,
|
|
496
|
+
reference_date: str | datetime.date | pd.Timestamp = None,
|
|
497
|
+
reference_col: str = None,
|
|
498
|
+
linebreak: str = '\n',
|
|
499
|
+
verbosity: int = 3,
|
|
501
500
|
):
|
|
502
501
|
"""
|
|
503
502
|
Calculates the number of days
|
|
504
503
|
between a reference date or col
|
|
505
|
-
and all other cols in the df.
|
|
504
|
+
and all other date cols in the df.
|
|
506
505
|
"""
|
|
507
506
|
|
|
508
|
-
df = df.copy().map(date_).convert_dtypes()
|
|
509
|
-
|
|
510
|
-
for col in df.columns:
|
|
511
|
-
if df[col].isna().all():
|
|
512
|
-
df.drop(columns=col, inplace=True)
|
|
513
|
-
|
|
514
|
-
|
|
515
507
|
if reference_date is None and reference_col is None:
|
|
516
508
|
msg = 'ERROR: no reference date or column provided'
|
|
517
509
|
log(msg, 'dk.date_delta', verbosity)
|
|
@@ -522,6 +514,27 @@ def date_delta(
|
|
|
522
514
|
log(msg, 'dk.date_delta', verbosity)
|
|
523
515
|
return df
|
|
524
516
|
|
|
517
|
+
|
|
518
|
+
df = df.copy()
|
|
519
|
+
df_dates = (
|
|
520
|
+
df
|
|
521
|
+
.copy()
|
|
522
|
+
.map(date_)
|
|
523
|
+
.convert_dtypes()
|
|
524
|
+
)
|
|
525
|
+
cols_dates = []
|
|
526
|
+
cols_all = list(df.columns)
|
|
527
|
+
|
|
528
|
+
for col in df_dates.columns:
|
|
529
|
+
if df_dates[col].isna().all():
|
|
530
|
+
df_dates.drop(columns=col, inplace=True)
|
|
531
|
+
else:
|
|
532
|
+
cols_dates.append(col)
|
|
533
|
+
df.drop(columns=col, inplace=True)
|
|
534
|
+
|
|
535
|
+
df = pd.concat([df, df_dates], axis=1)[cols_all]
|
|
536
|
+
|
|
537
|
+
|
|
525
538
|
if 'reference_date' in df.columns:
|
|
526
539
|
msg = 'WARNING: column "reference_date" already exists, overwriting'
|
|
527
540
|
log(msg, 'dk.date_delta', verbosity)
|
|
@@ -531,20 +544,16 @@ def date_delta(
|
|
|
531
544
|
else:
|
|
532
545
|
df['reference_date'] = df[reference_col].apply(date_)
|
|
533
546
|
|
|
534
|
-
cols_reorder = [
|
|
535
|
-
cols_reorder += [
|
|
536
|
-
col
|
|
537
|
-
for col
|
|
538
|
-
in df.columns
|
|
539
|
-
if col != 'reference_date'
|
|
540
|
-
]
|
|
547
|
+
cols_reorder = [col for col in df.columns]
|
|
541
548
|
df = df[cols_reorder]
|
|
542
549
|
|
|
543
550
|
for col in df.columns:
|
|
544
|
-
if col
|
|
551
|
+
if col not in cols_dates:
|
|
552
|
+
continue
|
|
553
|
+
if col in ['reference_date', reference_col]:
|
|
545
554
|
continue
|
|
546
555
|
|
|
547
|
-
name = f'days from
|
|
556
|
+
name = f'days from{linebreak}{reference_col}{linebreak}to{linebreak}{col}'
|
|
548
557
|
if name in df.columns:
|
|
549
558
|
log(f'WARNING: column "{name}" already exists, overwriting',
|
|
550
559
|
'dk.date_delta', verbosity)
|
|
@@ -554,10 +563,161 @@ def date_delta(
|
|
|
554
563
|
#_fix_nas converts accordingly
|
|
555
564
|
col_formatted = df[col].apply(date_).apply(_fix_nas)
|
|
556
565
|
col_diff = col_formatted - df['reference_date']
|
|
557
|
-
df[name] =
|
|
566
|
+
df[name] = (
|
|
567
|
+
col_diff
|
|
568
|
+
.fillna(pd.NaT) #type:ignore (pylance doesnt know date_ enables this)
|
|
569
|
+
.apply(lambda x: x.days)
|
|
570
|
+
)
|
|
558
571
|
|
|
559
572
|
return df
|
|
560
573
|
|
|
561
574
|
|
|
562
575
|
def _fix_nas(x):
|
|
563
576
|
return pd.NA if x is pd.NaT else x
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
def date_table(
|
|
582
|
+
df: pd.DataFrame,
|
|
583
|
+
reference_col: str = None,
|
|
584
|
+
uid: str = None,
|
|
585
|
+
upper: int = None,
|
|
586
|
+
lower: int = None,
|
|
587
|
+
start_at_day1: bool = True,
|
|
588
|
+
schedule: dict[int, str] = None,
|
|
589
|
+
linebreak: str = '\n',
|
|
590
|
+
verbosity: int = 3,
|
|
591
|
+
) -> pd.DataFrame | pd.io.formats.style.Styler:
|
|
592
|
+
"""
|
|
593
|
+
Arranges all dates into a table,
|
|
594
|
+
with positions relative to a
|
|
595
|
+
reference date or column.
|
|
596
|
+
"""
|
|
597
|
+
|
|
598
|
+
if reference_col is None:
|
|
599
|
+
msg = 'ERROR: no reference column provided'
|
|
600
|
+
log(msg, 'dk.date_delta', verbosity)
|
|
601
|
+
return pd.DataFrame()
|
|
602
|
+
|
|
603
|
+
df = df.copy()
|
|
604
|
+
if uid:
|
|
605
|
+
df.index = df[uid]
|
|
606
|
+
reference = df[reference_col].apply(date_)
|
|
607
|
+
|
|
608
|
+
df = (
|
|
609
|
+
df
|
|
610
|
+
.copy()
|
|
611
|
+
.map(date_)
|
|
612
|
+
.convert_dtypes()
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
for col in df.columns:
|
|
616
|
+
if df[col].isna().all():
|
|
617
|
+
df.drop(columns=col, inplace=True)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
blanks = {col: pd.NaT for col in df.columns}
|
|
621
|
+
deltas = pd.DataFrame(
|
|
622
|
+
blanks,
|
|
623
|
+
index=df.index,
|
|
624
|
+
)
|
|
625
|
+
|
|
626
|
+
for col in df.columns:
|
|
627
|
+
#difference only works with pd.NA,
|
|
628
|
+
#but x.days only works with pd.NaT.
|
|
629
|
+
#_fix_nas converts accordingly
|
|
630
|
+
col_formatted = df[col].apply(date_).apply(_fix_nas)
|
|
631
|
+
col_diff = col_formatted - reference
|
|
632
|
+
deltas[col] = (
|
|
633
|
+
col_diff
|
|
634
|
+
.fillna(pd.NaT) #type:ignore (pylance doesnt know date_ enables this)
|
|
635
|
+
.apply(lambda x: x.days)
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
deltas = deltas.T
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
if upper is None:
|
|
642
|
+
upper = int(deltas.max().max()) + 1
|
|
643
|
+
if lower is None:
|
|
644
|
+
lower = int(deltas.min().min())
|
|
645
|
+
|
|
646
|
+
days = list(range(lower, upper + 1))
|
|
647
|
+
if start_at_day1:
|
|
648
|
+
days.remove(0)
|
|
649
|
+
else:
|
|
650
|
+
days.pop(-1)
|
|
651
|
+
days_df = pd.DataFrame({'days': days})
|
|
652
|
+
|
|
653
|
+
df_timeline = days_df.copy()
|
|
654
|
+
if schedule is not None:
|
|
655
|
+
df_schedule = pd.DataFrame({
|
|
656
|
+
'days': list(schedule.keys()),
|
|
657
|
+
'planned': list(schedule.values()),
|
|
658
|
+
})
|
|
659
|
+
df_timeline = df_timeline.merge(
|
|
660
|
+
df_schedule,
|
|
661
|
+
on='days',
|
|
662
|
+
how='left',
|
|
663
|
+
).fillna('')
|
|
664
|
+
|
|
665
|
+
for col in deltas.columns:
|
|
666
|
+
events = pd.DataFrame({
|
|
667
|
+
'days': deltas[col].astype('Int64'),
|
|
668
|
+
col: deltas.index,
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
if start_at_day1:
|
|
672
|
+
events = _shift_day0(events)
|
|
673
|
+
|
|
674
|
+
merged = days_df.copy().merge(events, on='days', how='left').fillna('')
|
|
675
|
+
collapsed = collapse(
|
|
676
|
+
merged,
|
|
677
|
+
on='days',
|
|
678
|
+
template_item='{item}' + linebreak,
|
|
679
|
+
)
|
|
680
|
+
df_timeline = df_timeline.merge(
|
|
681
|
+
collapsed,
|
|
682
|
+
on='days',
|
|
683
|
+
how='left',
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
if schedule is not None:
|
|
687
|
+
df_timeline = _highlight_schedule(df_timeline, schedule)
|
|
688
|
+
|
|
689
|
+
return df_timeline
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def _shift_day0(df: pd.DataFrame) -> pd.DataFrame:
|
|
693
|
+
if 'days' not in df.columns:
|
|
694
|
+
return df
|
|
695
|
+
if 0 not in df['days'].values:
|
|
696
|
+
return df
|
|
697
|
+
mask_positive = df['days'] >= 0
|
|
698
|
+
df.loc[mask_positive, 'days'] += 1
|
|
699
|
+
return df
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
def _highlight_schedule(
|
|
703
|
+
df: pd.DataFrame,
|
|
704
|
+
schedule: dict,
|
|
705
|
+
) -> pd.io.formats.style.Styler:
|
|
706
|
+
|
|
707
|
+
days_highlight = list(schedule.keys())
|
|
708
|
+
|
|
709
|
+
df_style = pd.DataFrame(
|
|
710
|
+
'text-align: left;',
|
|
711
|
+
columns=df.columns,
|
|
712
|
+
index=df.index
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
mask_highlight = df['days'].isin(days_highlight)
|
|
716
|
+
df_style.loc[mask_highlight, :] += f'background-color: {GREEN_LIGHT};'
|
|
717
|
+
|
|
718
|
+
df_styled = df.style.apply(
|
|
719
|
+
lambda x: df_style,
|
|
720
|
+
axis=None,
|
|
721
|
+
)
|
|
722
|
+
|
|
723
|
+
return df_styled
|
|
@@ -4015,6 +4015,7 @@ class SetTypeInfo(Symbol):
|
|
|
4015
4015
|
category = 'setter'
|
|
4016
4016
|
regex = (
|
|
4017
4017
|
r'\.typeinfo',
|
|
4018
|
+
r'\.type',
|
|
4018
4019
|
)
|
|
4019
4020
|
|
|
4020
4021
|
#used to validate the current op
|
|
@@ -6075,8 +6076,20 @@ class ViewParserHelp(Symbol):
|
|
|
6075
6076
|
)
|
|
6076
6077
|
print('\n\n')
|
|
6077
6078
|
|
|
6079
|
+
|
|
6078
6080
|
if not q.op.operator:
|
|
6079
6081
|
|
|
6082
|
+
flags = _get_valid_operators(
|
|
6083
|
+
q.op,
|
|
6084
|
+
'flags',
|
|
6085
|
+
cols_show
|
|
6086
|
+
)
|
|
6087
|
+
_print_or_display(
|
|
6088
|
+
'flags:',
|
|
6089
|
+
flags[cols_show],
|
|
6090
|
+
)
|
|
6091
|
+
print('\n\n')
|
|
6092
|
+
|
|
6080
6093
|
generic_ops = _get_valid_operators(
|
|
6081
6094
|
q.op,
|
|
6082
6095
|
'generic',
|
|
@@ -6657,7 +6670,7 @@ class FlagNegate(Symbol):
|
|
|
6657
6670
|
>>> df.dk.qs(r'id %%!>20000')
|
|
6658
6671
|
"""
|
|
6659
6672
|
name = 'FlagNegate'
|
|
6660
|
-
category = '
|
|
6673
|
+
category = 'flag'
|
|
6661
6674
|
regex = (r'!',)
|
|
6662
6675
|
|
|
6663
6676
|
def parse(self, q: Query) -> Query:
|
|
@@ -6668,6 +6681,28 @@ class FlagNegate(Symbol):
|
|
|
6668
6681
|
|
|
6669
6682
|
|
|
6670
6683
|
|
|
6684
|
+
class FlagIndex(Symbol):
|
|
6685
|
+
"""
|
|
6686
|
+
Condition is applied to the index
|
|
6687
|
+
instead of the values.
|
|
6688
|
+
|
|
6689
|
+
Examples
|
|
6690
|
+
--------
|
|
6691
|
+
>>> df.dk.qs(r'%§0')
|
|
6692
|
+
>>> df.dk.qs(r'name §3')
|
|
6693
|
+
"""
|
|
6694
|
+
name = 'FlagIndex'
|
|
6695
|
+
category = 'flag'
|
|
6696
|
+
regex = (r'§',)
|
|
6697
|
+
|
|
6698
|
+
def parse(self, q: Query) -> Query:
|
|
6699
|
+
q = _preparse_for_getter(q)
|
|
6700
|
+
q.op.flags['index'] = 'apply condition to the index'
|
|
6701
|
+
return q
|
|
6702
|
+
|
|
6703
|
+
|
|
6704
|
+
|
|
6705
|
+
|
|
6671
6706
|
class FlagColref(Symbol):
|
|
6672
6707
|
"""
|
|
6673
6708
|
interpret args as col references
|
|
@@ -6678,7 +6713,7 @@ class FlagColref(Symbol):
|
|
|
6678
6713
|
>>> df.dk.qs(r'age <@height')
|
|
6679
6714
|
"""
|
|
6680
6715
|
name = 'FlagColref'
|
|
6681
|
-
category = '
|
|
6716
|
+
category = 'flag'
|
|
6682
6717
|
regex = (r'@',)
|
|
6683
6718
|
|
|
6684
6719
|
def parse(self, q: Query) -> Query:
|
|
@@ -6706,7 +6741,7 @@ class Flag(Symbol):
|
|
|
6706
6741
|
>>> df.dk.qs(r'age %%>30 +strict')
|
|
6707
6742
|
"""
|
|
6708
6743
|
name = 'Flag'
|
|
6709
|
-
category = '
|
|
6744
|
+
category = 'flag'
|
|
6710
6745
|
regex = (r'\+\w+',)
|
|
6711
6746
|
|
|
6712
6747
|
def build(self, str_matched: str) -> 'Symbol':
|
|
@@ -6735,6 +6770,7 @@ getters = []
|
|
|
6735
6770
|
setters = []
|
|
6736
6771
|
stylers = []
|
|
6737
6772
|
viewers = []
|
|
6773
|
+
flags = []
|
|
6738
6774
|
for _obj in list(locals().values()):
|
|
6739
6775
|
if isinstance(_obj, type) and issubclass(_obj, Symbol):
|
|
6740
6776
|
if _obj is not Symbol:
|
|
@@ -6758,6 +6794,8 @@ for _obj in list(locals().values()):
|
|
|
6758
6794
|
elif _obj.category == 'viewer':
|
|
6759
6795
|
operators.append(_obj())
|
|
6760
6796
|
viewers.append(_obj())
|
|
6797
|
+
elif _obj.category == 'flag':
|
|
6798
|
+
flags.append(_obj())
|
|
6761
6799
|
|
|
6762
6800
|
|
|
6763
6801
|
|
|
@@ -6782,6 +6820,7 @@ def as_df(category='all') -> pd.DataFrame:
|
|
|
6782
6820
|
'setter',
|
|
6783
6821
|
'styler',
|
|
6784
6822
|
'viewer',
|
|
6823
|
+
'flag',
|
|
6785
6824
|
)
|
|
6786
6825
|
if category in singular:
|
|
6787
6826
|
category += 's'
|
|
@@ -6804,6 +6843,8 @@ def as_df(category='all') -> pd.DataFrame:
|
|
|
6804
6843
|
symbols = stylers
|
|
6805
6844
|
elif category == 'viewers':
|
|
6806
6845
|
symbols = viewers
|
|
6846
|
+
elif category == 'flags':
|
|
6847
|
+
symbols = flags
|
|
6807
6848
|
else:
|
|
6808
6849
|
raise ValueError(f"Unknown category: {category}")
|
|
6809
6850
|
|
|
@@ -6813,7 +6854,7 @@ def as_df(category='all') -> pd.DataFrame:
|
|
|
6813
6854
|
descriptions = [_get_description(d) for d in docs]
|
|
6814
6855
|
examples = [_get_example(d) for d in docs]
|
|
6815
6856
|
categories = [s.category for s in symbols]
|
|
6816
|
-
|
|
6857
|
+
flags_actual = ['\n'.join(s.flags) for s in symbols]
|
|
6817
6858
|
flags_allowed = ['\n'.join(s.flags_allowed) for s in symbols]
|
|
6818
6859
|
scopes_allowed = ['\n'.join(s.scopes_allowed) for s in symbols]
|
|
6819
6860
|
connectors_allowed = ['\n'.join(s.connectors_allowed) for s in symbols]
|
|
@@ -6823,7 +6864,7 @@ def as_df(category='all') -> pd.DataFrame:
|
|
|
6823
6864
|
'description': descriptions,
|
|
6824
6865
|
'example': examples,
|
|
6825
6866
|
'category': categories,
|
|
6826
|
-
'flags':
|
|
6867
|
+
'flags': flags_actual,
|
|
6827
6868
|
'flags_allowed': flags_allowed,
|
|
6828
6869
|
'scopes_allowed': scopes_allowed,
|
|
6829
6870
|
'connectors_allowed': connectors_allowed,
|
|
@@ -6844,7 +6885,17 @@ def as_df_styled(category='all') -> pd.io.formats.style.Styler:
|
|
|
6844
6885
|
|
|
6845
6886
|
|
|
6846
6887
|
def _get_lexemes(symbol: Symbol) -> str:
|
|
6847
|
-
|
|
6888
|
+
show_raw = [
|
|
6889
|
+
'Newline',
|
|
6890
|
+
'Literal',
|
|
6891
|
+
'Whitespace',
|
|
6892
|
+
'Comment',
|
|
6893
|
+
'Flag',
|
|
6894
|
+
]
|
|
6895
|
+
if symbol.name in show_raw:
|
|
6896
|
+
string = '\n'.join(symbol.regex)
|
|
6897
|
+
else:
|
|
6898
|
+
string = '\n'.join(symbol.regex).replace('\\', '')
|
|
6848
6899
|
return string
|
|
6849
6900
|
|
|
6850
6901
|
|
|
@@ -300,7 +300,7 @@ def date_(x, errors='coerce', na=pd.NaT) -> typing.Any:
|
|
|
300
300
|
return result.date()
|
|
301
301
|
|
|
302
302
|
#raise and coerce are handled in _datetime and should not be reached here
|
|
303
|
-
elif errors == 'raise':
|
|
303
|
+
elif errors == 'raise':
|
|
304
304
|
raise ValueError(
|
|
305
305
|
f'could not convert "{x}" to datetime.\n'
|
|
306
306
|
'Error handling:\n'
|
|
@@ -312,7 +312,7 @@ def date_(x, errors='coerce', na=pd.NaT) -> typing.Any:
|
|
|
312
312
|
elif errors == 'ignore':
|
|
313
313
|
return x
|
|
314
314
|
elif errors == 'coerce':
|
|
315
|
-
return na
|
|
315
|
+
return na
|
|
316
316
|
else:
|
|
317
317
|
return errors
|
|
318
318
|
|
|
@@ -164,7 +164,7 @@ def log(
|
|
|
164
164
|
logs.append(message)
|
|
165
165
|
|
|
166
166
|
#for jupyter
|
|
167
|
-
if get_ipython().__class__.__name__ == 'ZMQInteractiveShell':
|
|
167
|
+
if get_ipython().__class__.__name__ == 'ZMQInteractiveShell':
|
|
168
168
|
|
|
169
169
|
#make html friendly
|
|
170
170
|
message['text'] = (
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|