77
88from pointblank .column import Column
99from pointblank .schema import Schema
10+ from pointblank .validate import _create_table_type_html , _create_table_dims_html
1011from pointblank ._utils import _get_tbl_type , _check_any_df_lib , _select_df_lib
1112
1213__all__ = ["preview" ]
@@ -19,6 +20,7 @@ def preview(
1920 n_tail : int = 5 ,
2021 limit : int | None = 50 ,
2122 show_row_numbers : bool = True ,
23+ incl_header : bool = True ,
2224 max_col_width : int | None = 250 ,
2325) -> GT :
2426 """
@@ -59,6 +61,9 @@ def preview(
5961 show_row_numbers
6062 Should row numbers be shown? The numbers shown reflect the row numbers of the head and tail
6163 in the full table.
64+ incl_header
65+ Should the table include a header with the table type and table dimensions? Set to `True` by
66+ default.
6267 max_col_width
6368 The maximum width of the columns in pixels. This is `250` (`"250px"`) by default.
6469
@@ -176,6 +181,12 @@ def preview(
176181 if pl_pb_tbl :
177182 df_lib_name_gt = "polars" if "polars" in tbl_type else "pandas"
178183
184+ # Handle imports of Polars or Pandas here
185+ if df_lib_name_gt == "polars" :
186+ import polars as pl
187+ else :
188+ import pandas as pd
189+
179190 # If `columns_subset=` is not None, resolve the columns to display
180191 if columns_subset is not None :
181192
@@ -240,8 +251,6 @@ def preview(
240251
241252 if tbl_type == "polars" :
242253
243- import polars as pl
244-
245254 n_rows = int (data .height )
246255
247256 # If n_head + n_tail is greater than the row count, display the entire table
@@ -257,8 +266,6 @@ def preview(
257266
258267 if tbl_type == "pandas" :
259268
260- import pandas as pd
261-
262269 n_rows = data .shape [0 ]
263270
264271 # If n_head + n_tail is greater than the row count, display the entire table
@@ -289,6 +296,16 @@ def preview(
289296 k : v .split ("(" )[0 ] if "(" in v else v for k , v in col_dtype_dict .items ()
290297 }
291298
299+ # Create a dictionary of column and row positions where the value is None/NA/NULL
300+ # This is used to highlight these values in the table
301+ if df_lib_name_gt == "polars" :
302+ none_values = {k : data [k ].is_null ().to_list () for k in col_names }
303+ else :
304+ none_values = {k : data [k ].isnull () for k in col_names }
305+
306+ none_values = [(k , i ) for k , v in none_values .items () for i , val in enumerate (v ) if val ]
307+
308+ # Import Great Tables to get preliminary renders of the columns
292309 import great_tables as gt
293310
294311 # For each of the columns get the average number of characters printed for each of the values
@@ -342,8 +359,6 @@ def preview(
342359
343360 if df_lib_name_gt == "pandas" :
344361
345- import pandas as pd
346-
347362 data .insert (0 , "_row_num_" , row_number_list )
348363
349364 # Get the highest number in the `row_number_list` and calculate a width that will
@@ -356,9 +371,29 @@ def preview(
356371 # Update the col_dtype_labels_dict to include the row number column (use empty string)
357372 col_dtype_labels_dict = {"_row_num_" : "" } | col_dtype_labels_dict
358373
374+ # Create the label, table type, and thresholds HTML fragments
375+ table_type_html = _create_table_type_html (
376+ tbl_type = tbl_type , tbl_name = None , font_size = "10px"
377+ )
378+
379+ tbl_dims_html = _create_table_dims_html (
380+ columns = len (col_names ), rows = n_rows , font_size = "10px"
381+ )
382+
383+ # Compose the subtitle HTML fragment
384+ combined_subtitle = (
385+ "<div>"
386+ '<div style="padding-top: 0; padding-bottom: 7px;">'
387+ f"{ table_type_html } "
388+ f"{ tbl_dims_html } "
389+ "</div>"
390+ "</div>"
391+ )
392+
359393 gt_tbl = (
360394 GT (data = data , id = "pb_preview_tbl" )
361395 .opt_table_font (font = google_font (name = "IBM Plex Sans" ))
396+ .opt_align_table_header (align = "left" )
362397 .fmt_markdown (columns = col_names )
363398 .tab_style (
364399 style = style .css (
@@ -393,6 +428,23 @@ def preview(
393428 .cols_width (cases = col_width_dict )
394429 )
395430
431+ if incl_header :
432+ gt_tbl = gt_tbl .tab_header (title = html (combined_subtitle ))
433+ gt_tbl = gt_tbl .tab_options (heading_subtitle_font_size = "12px" )
434+
435+ if none_values :
436+ for column , none_index in none_values :
437+ gt_tbl = gt_tbl .tab_style (
438+ style = [style .text (color = "#B22222" ), style .fill (color = "#FFC1C159" )],
439+ locations = loc .body (rows = none_index , columns = column ),
440+ )
441+
442+ if tbl_type == "pandas" :
443+ gt_tbl = gt_tbl .sub_missing (missing_text = "NA" )
444+
445+ if ibis_tbl :
446+ gt_tbl = gt_tbl .sub_missing (missing_text = "NULL" )
447+
396448 if not full_dataset :
397449
398450 gt_tbl = gt_tbl .tab_style (
0 commit comments