Skip to content

Commit 13f86f4

Browse files
authored
Merge pull request #93 from posit-dev/feat-col-summary-tbl-improve
feat: improve appearance of `col_summary_tbl()` report table
2 parents 0551e92 + fef3aad commit 13f86f4

1 file changed

Lines changed: 106 additions & 39 deletions

File tree

pointblank/datascan.py

Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -632,10 +632,10 @@ def get_tabular_report(self) -> GT:
632632

633633
# Iterate over each column's data and obtain a dictionary of statistics for each column
634634
for idx, col in enumerate(column_data):
635-
if "statistics" in col and (
636-
"numerical" in col["statistics"] or "string_lengths" in col["statistics"]
637-
):
638-
col_dict = _process_numerical_string_column_data(col)
635+
if "statistics" in col and "numerical" in col["statistics"]:
636+
col_dict = _process_numerical_column_data(col)
637+
elif "statistics" in col and "string_lengths" in col["statistics"]:
638+
col_dict = _process_string_column_data(col)
639639
elif "statistics" in col and "datetime" in col["statistics"]:
640640
col_dict = _process_datetime_column_data(col)
641641
datetime_row_list.append(idx)
@@ -724,13 +724,11 @@ def get_tabular_report(self) -> GT:
724724
)
725725
.tab_style(
726726
style=style.borders(sides="left", color="#D3D3D3", style="solid"),
727-
locations=loc.body(columns=["missing_vals", "mean", "iqr"]),
727+
locations=loc.body(columns=["missing_vals", "mean", "min", "iqr"]),
728728
)
729729
.tab_style(
730730
style=style.borders(sides="left", color="#E5E5E5", style="dashed"),
731-
locations=loc.body(
732-
columns=["std_dev", "min", "p05", "q_1", "med", "q_3", "p95", "max"]
733-
),
731+
locations=loc.body(columns=["std_dev", "p05", "q_1", "med", "q_3", "p95", "max"]),
734732
)
735733
.tab_style(
736734
style=style.borders(sides="left", style="none"),
@@ -743,20 +741,31 @@ def get_tabular_report(self) -> GT:
743741
style=style.fill(color="#FCFCFC"),
744742
locations=loc.body(columns=["missing_vals", "unique_vals", "iqr"]),
745743
)
744+
.tab_style(
745+
style=style.text(align="center"), locations=loc.column_labels(columns=stat_columns)
746+
)
746747
.cols_label(
747748
column_number="",
748749
icon="",
749750
column_name="Column",
750-
missing_vals="NAs",
751-
unique_vals="Uniq.",
751+
missing_vals="NA",
752+
unique_vals="UQ",
752753
mean="Mean",
753-
std_dev="S.D.",
754+
std_dev="SD",
754755
min="Min",
755-
p05="P05",
756-
q_1="Q1",
756+
p05=html(
757+
'P<span style="font-size: 0.75em; vertical-align: sub; position: relative; line-height: 0.5em;">5</span>'
758+
),
759+
q_1=html(
760+
'Q<span style="font-size: 0.75em; vertical-align: sub; position: relative; line-height: 0.5em;">1</span>'
761+
),
757762
med="Med",
758-
q_3="Q3",
759-
p95="P95",
763+
q_3=html(
764+
'Q<span style="font-size: 0.75em; vertical-align: sub; position: relative; line-height: 0.5em;">3</span>'
765+
),
766+
p95=html(
767+
'P<span style="font-size: 0.75em; vertical-align: sub; position: relative; line-height: 0.5em;">95</span>'
768+
),
760769
max="Max",
761770
iqr="IQR",
762771
)
@@ -910,10 +919,12 @@ def _compact_decimal_fmt(value: float | int) -> str:
910919
formatted = fmt_number(value, decimals=2)[0]
911920
elif abs(value) < 0.01:
912921
formatted = fmt_scientific(value, decimals=1, exp_style="E1")[0]
913-
elif abs(value) >= 1 and abs(value) < 1000:
922+
elif abs(value) >= 1 and abs(value) < 10:
923+
formatted = fmt_number(value, decimals=2, use_seps=False)[0]
924+
elif abs(value) >= 10 and abs(value) < 1000:
914925
formatted = fmt_number(value, n_sigfig=3)[0]
915926
elif abs(value) >= 1000 and abs(value) < 10_000:
916-
formatted = fmt_number(value, decimals=0, use_seps=False)[0]
927+
formatted = fmt_number(value, n_sigfig=4, use_seps=False)[0]
917928
else:
918929
formatted = fmt_scientific(value, decimals=1, exp_style="E1")[0]
919930

@@ -937,7 +948,7 @@ def _compact_0_1_fmt(value: float | int) -> str:
937948
return formatted
938949

939950

940-
def _process_numerical_string_column_data(column_data: dict) -> dict:
951+
def _process_numerical_column_data(column_data: dict) -> dict:
941952
column_number = column_data["column_number"]
942953
column_name = column_data["column_name"]
943954
column_type = column_data["column_type"]
@@ -947,26 +958,18 @@ def _process_numerical_string_column_data(column_data: dict) -> dict:
947958
f"<div style='font-size: 11px; color: gray;'>{column_type}</div>"
948959
)
949960

950-
# Determine if the column is a numerical or string column
951-
if "numerical" in column_data["statistics"]:
952-
key = "numerical"
953-
icon = "numeric"
954-
elif "string_lengths" in column_data["statistics"]:
955-
key = "string_lengths"
956-
icon = "string"
957-
958961
# Get the Missing and Unique value counts and fractions
959962
missing_vals = column_data["n_missing_values"]
960963
unique_vals = column_data["n_unique_values"]
961-
missing_vals_frac = _compact_decimal_fmt(column_data["f_missing_values"])
962-
unique_vals_frac = _compact_decimal_fmt(column_data["f_unique_values"])
964+
missing_vals_frac = _compact_0_1_fmt(column_data["f_missing_values"])
965+
unique_vals_frac = _compact_0_1_fmt(column_data["f_unique_values"])
963966

964967
missing_vals_str = f"{missing_vals}<br>{missing_vals_frac}"
965968
unique_vals_str = f"{unique_vals}<br>{unique_vals_frac}"
966969

967970
# Get the descriptive and quantile statistics
968-
descriptive_stats = column_data["statistics"][key]["descriptive"]
969-
quantile_stats = column_data["statistics"][key]["quantiles"]
971+
descriptive_stats = column_data["statistics"]["numerical"]["descriptive"]
972+
quantile_stats = column_data["statistics"]["numerical"]["quantiles"]
970973

971974
# Get all values from the descriptive and quantile stats into a single list
972975
quantile_stats_vals = [v[1] for v in quantile_stats.items()]
@@ -1000,7 +1003,7 @@ def _process_numerical_string_column_data(column_data: dict) -> dict:
10001003
# Create a single dictionary with the statistics for the column
10011004
stats_dict = {
10021005
"column_number": column_number,
1003-
"icon": SVG_ICONS_FOR_DATA_TYPES[icon],
1006+
"icon": SVG_ICONS_FOR_DATA_TYPES["numeric"],
10041007
"column_name": column_name_and_type,
10051008
"missing_vals": missing_vals_str,
10061009
"unique_vals": unique_vals_str,
@@ -1011,6 +1014,68 @@ def _process_numerical_string_column_data(column_data: dict) -> dict:
10111014
return stats_dict
10121015

10131016

1017+
def _process_string_column_data(column_data: dict) -> dict:
1018+
column_number = column_data["column_number"]
1019+
column_name = column_data["column_name"]
1020+
column_type = column_data["column_type"]
1021+
1022+
column_name_and_type = (
1023+
f"<div style='font-size: 13px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;'>{column_name}</div>"
1024+
f"<div style='font-size: 11px; color: gray;'>{column_type}</div>"
1025+
)
1026+
1027+
# Get the Missing and Unique value counts and fractions
1028+
missing_vals = column_data["n_missing_values"]
1029+
unique_vals = column_data["n_unique_values"]
1030+
missing_vals_frac = _compact_0_1_fmt(column_data["f_missing_values"])
1031+
unique_vals_frac = _compact_0_1_fmt(column_data["f_unique_values"])
1032+
1033+
missing_vals_str = f"{missing_vals}<br>{missing_vals_frac}"
1034+
unique_vals_str = f"{unique_vals}<br>{unique_vals_frac}"
1035+
1036+
# Get the descriptive and quantile statistics
1037+
descriptive_stats = column_data["statistics"]["string_lengths"]["descriptive"]
1038+
quantile_stats = column_data["statistics"]["string_lengths"]["quantiles"]
1039+
1040+
# Format the descriptive statistics (mean and standard deviation)
1041+
for key, value in descriptive_stats.items():
1042+
formatted_val = _compact_decimal_fmt(value=value)
1043+
descriptive_stats[key] = (
1044+
f'<div><div>{formatted_val}</div><div style="float: left; position: absolute;">'
1045+
'<div title="string length measure" style="font-size: 7px; color: #999; '
1046+
'font-style: italic; cursor: help;">SL</div></div></div>'
1047+
)
1048+
1049+
# Format the quantile statistics
1050+
for key, value in quantile_stats.items():
1051+
formatted_val = _compact_integer_fmt(value=value)
1052+
quantile_stats[key] = (
1053+
f'<div><div>{formatted_val}</div><div style="float: left; position: absolute;">'
1054+
'<div title="string length measure" style="font-size: 7px; color: #999; '
1055+
'font-style: italic; cursor: help;">SL</div></div></div>'
1056+
)
1057+
1058+
# Create a single dictionary with the statistics for the column
1059+
stats_dict = {
1060+
"column_number": column_number,
1061+
"icon": SVG_ICONS_FOR_DATA_TYPES["string"],
1062+
"column_name": column_name_and_type,
1063+
"missing_vals": missing_vals_str,
1064+
"unique_vals": unique_vals_str,
1065+
**descriptive_stats,
1066+
"min": quantile_stats["min"],
1067+
"p05": "&mdash;",
1068+
"q_1": "&mdash;",
1069+
"med": quantile_stats["med"],
1070+
"q_3": "&mdash;",
1071+
"p95": "&mdash;",
1072+
"max": quantile_stats["max"],
1073+
"iqr": "&mdash;",
1074+
}
1075+
1076+
return stats_dict
1077+
1078+
10141079
def _process_datetime_column_data(column_data: dict) -> dict:
10151080
column_number = column_data["column_number"]
10161081
column_name = column_data["column_name"]
@@ -1031,8 +1096,8 @@ def _process_datetime_column_data(column_data: dict) -> dict:
10311096
# Get the Missing and Unique value counts and fractions
10321097
missing_vals = column_data["n_missing_values"]
10331098
unique_vals = column_data["n_unique_values"]
1034-
missing_vals_frac = _compact_decimal_fmt(column_data["f_missing_values"])
1035-
unique_vals_frac = _compact_decimal_fmt(column_data["f_unique_values"])
1099+
missing_vals_frac = _compact_0_1_fmt(column_data["f_missing_values"])
1100+
unique_vals_frac = _compact_0_1_fmt(column_data["f_unique_values"])
10361101

10371102
missing_vals_str = f"{missing_vals}<br>{missing_vals_frac}"
10381103
unique_vals_str = f"{unique_vals}<br>{unique_vals_frac}"
@@ -1076,9 +1141,9 @@ def _process_boolean_column_data(column_data: dict) -> dict:
10761141
f"<div style='font-size: 11px; color: gray;'>{column_type}</div>"
10771142
)
10781143

1079-
# Get the Missing and Unique value counts and fractions
1144+
# Get the missing value count and fraction
10801145
missing_vals = column_data["n_missing_values"]
1081-
missing_vals_frac = _compact_decimal_fmt(column_data["f_missing_values"])
1146+
missing_vals_frac = _compact_0_1_fmt(column_data["f_missing_values"])
10821147
missing_vals_str = f"{missing_vals}<br>{missing_vals_frac}"
10831148

10841149
# Get the fractions of True and False values
@@ -1088,10 +1153,12 @@ def _process_boolean_column_data(column_data: dict) -> dict:
10881153
true_vals_frac_fmt = _compact_0_1_fmt(f_true_values)
10891154
false_vals_frac_fmt = _compact_0_1_fmt(f_false_values)
10901155

1091-
# Create an HTML string that combines fractions for the True and False values
1092-
true_false_vals_str = f"<span style='font-weight: bold;'>T</span>{true_vals_frac_fmt}<br><span style='font-weight: bold;'>F</span>{false_vals_frac_fmt}"
1093-
1094-
# unique_vals_str = f"{unique_vals}<br>{unique_vals_frac}"
1156+
# Create an HTML string that combines fractions for the True and False values; this will be
1157+
# used in the Unique Vals column of the report table
1158+
true_false_vals_str = (
1159+
f"<span style='font-weight: bold;'>T</span>{true_vals_frac_fmt}<br>"
1160+
f"<span style='font-weight: bold;'>F</span>{false_vals_frac_fmt}"
1161+
)
10951162

10961163
# Create a single dictionary with the statistics for the column
10971164
stats_dict = {

0 commit comments

Comments
 (0)