Tables are fundamental to empirical research in finance. Whether you’re presenting summary statistics of your data, reporting regression results, or comparing model performance across specifications, clear and well-formatted tables communicate your findings effectively to readers. In this chapter, you’ll learn how to create publication-quality tables in Python and export them to formats suitable for academic papers.
The objectives of this chapter are to:
Create descriptive statistics tables that summarize key features of your data
Format regression output tables with proper presentation of coefficients, standard errors, and model statistics
Export tables to LaTeX format for inclusion in academic papers
Apply best practices for table design that meet journal formatting requirements
By the end of this chapter, you’ll be able to produce tables that look professional, convey information clearly, and can be easily integrated into your research papers. The emphasis is on practical workflows that save time while ensuring your tables meet the standards expected in top finance journals.
18.2 Prerequisites
This chapter assumes you are familiar with pandas DataFrames (covered in Introduction to DataFrames) and have basic knowledge of regression analysis. You should also have pandas and statsmodels installed in your Python environment. We’ll introduce additional packages for table formatting as we go.
18.3 Descriptive Statistics Tables
Descriptive statistics tables typically appear early in empirical papers, often labeled “Summary Statistics” or “Descriptive Statistics.” They provide readers with a quick overview of the key variables in your analysis: their central tendency, dispersion, and sometimes their distribution properties.
18.3.1 Basic Summary Statistics with pandas
The simplest way to generate summary statistics is using pandas’ built-in describe() method. Let’s start with a sample dataset of stock returns:
import pandas as pdimport numpy as np# Set random seed for reproducibilitynp.random.seed(42)# Create sample data: monthly returns for 5 stocks over 60 monthsstocks = ['AAPL', 'GOOGL', 'MSFT', 'AMZN', 'META']n_months =60data = {'Date': pd.date_range('2019-01-01', periods=n_months, freq='MS'),}for stock in stocks:# Generate returns with different means and volatilities mean_return = np.random.uniform(-0.01, 0.02) volatility = np.random.uniform(0.05, 0.10) data[stock] = np.random.normal(mean_return, volatility, n_months)df = pd.DataFrame(data)df = df.set_index('Date')# View first few rowsdf.head()
The describe() method provides count, mean, standard deviation, minimum, quartiles, and maximum. For financial data, we often want to customize this to show statistics more relevant to our analysis.
18.3.2 Customizing Summary Statistics
Let’s create a more informative summary statistics table tailored for financial returns:
Research papers often compare statistics across different groups (e.g., treated vs. control, large vs. small firms):
# Add a grouping variablepanel_df['size_group'] = pd.cut( panel_df['market_cap'], bins=2, labels=['Small', 'Large'])# Summary statistics by groupgrouped_stats = panel_df.groupby('size_group')[['returns', 'book_to_market']].agg([ ('Mean', 'mean'), ('Std Dev', 'std'), ('N', 'count')])# Flatten column namesgrouped_stats.columns = ['_'.join(col).strip() for col in grouped_stats.columns.values]grouped_stats
/var/folders/jr/cn9h86ld68qb5rtvs9gsb1vr0000gn/T/ipykernel_38836/3721035929.py:9: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.
grouped_stats = panel_df.groupby('size_group')[['returns', 'book_to_market']].agg([
returns_Mean
returns_Std Dev
returns_N
book_to_market_Mean
book_to_market_Std Dev
book_to_market_N
size_group
Small
0.010225
0.050226
599
0.987235
0.669736
599
Large
0.000245
NaN
1
0.557118
NaN
1
This type of table is common in papers examining differences between portfolios or firm characteristics.
18.3.6 Adding Statistical Tests to Summary Tables
When comparing groups, you often want to test whether differences are statistically significant:
from scipy import stats as scipy_statsdef add_difference_test(df, group_col, value_cols):""" Add t-test for difference in means between two groups. Parameters ---------- df : pd.DataFrame Panel data with grouping variable group_col : str Name of grouping column (must have exactly 2 groups) value_cols : list Variables to test Returns ------- pd.DataFrame Table with group means and difference tests """ groups = df[group_col].unique()iflen(groups) !=2:raiseValueError("Grouping variable must have exactly 2 groups") results = []for col in value_cols: group1_data = df[df[group_col] == groups[0]][col].dropna() group2_data = df[df[group_col] == groups[1]][col].dropna() mean1 = group1_data.mean() mean2 = group2_data.mean()# Two-sample t-test t_stat, p_value = scipy_stats.ttest_ind(group1_data, group2_data) results.append({'Variable': col,f'{groups[0]} Mean': f'{mean1:.4f}',f'{groups[1]} Mean': f'{mean2:.4f}','Difference': f'{mean1 - mean2:.4f}','t-stat': f'{t_stat:.3f}','p-value': f'{p_value:.3f}' })return pd.DataFrame(results)# Test differences between small and large firmsdiff_test = add_difference_test(panel_df, 'size_group', ['returns', 'book_to_market'])diff_test
Variable
Small Mean
Large Mean
Difference
t-stat
p-value
0
returns
0.0102
0.0002
0.0100
0.199
0.843
1
book_to_market
0.9872
0.5571
0.4301
0.642
0.521
NoteChoosing the Right Statistical Test
The t-test above assumes normally distributed data with equal variances. For financial data:
Use Welch’s t-test (default in scipy.stats.ttest_ind) if variances differ
Consider Mann-Whitney U test for non-normal distributions
Use paired t-test for matched samples (e.g., before/after comparisons)
Bootstrap standard errors for robust inference with small samples
18.4 Regression Output Tables
Regression results are the heart of most empirical papers in finance. A well-formatted regression table presents coefficients, standard errors (or t-statistics), and model fit statistics in a clear, compact format that allows readers to quickly assess your results.
18.4.1 Basic Regression with statsmodels
Let’s start by estimating a simple regression and examining the output:
The default summary from statsmodels is informative but not publication-ready. Let’s extract and format the key information.
18.4.2 Creating a Basic Regression Table
def create_regression_table(results):""" Create a formatted regression table from statsmodels results. Parameters ---------- results : statsmodels RegressionResults Fitted regression model Returns ------- pd.DataFrame Formatted regression table """# Extract coefficients and standard errors coefs = results.params std_errs = results.bse t_stats = results.tvalues p_values = results.pvalues# Create table table_data = []for var in coefs.index:# Format coefficient with standard error in parentheses coef_str =f"{coefs[var]:.4f}" se_str =f"({std_errs[var]:.4f})"# Add significance starsif p_values[var] <0.01: coef_str +="***"elif p_values[var] <0.05: coef_str +="**"elif p_values[var] <0.10: coef_str +="*" table_data.append({'Variable': var,'Coefficient': coef_str,'Std Error': se_str }) table = pd.DataFrame(table_data)# Add model statistics stats_rows = pd.DataFrame([ {'Variable': 'R-squared', 'Coefficient': f"{results.rsquared:.4f}", 'Std Error': ''}, {'Variable': 'Observations', 'Coefficient': str(int(results.nobs)), 'Std Error': ''} ]) table = pd.concat([table, stats_rows], ignore_index=True)return tablereg_table = create_regression_table(results)reg_table
Variable
Coefficient
Std Error
0
const
0.0041
(0.0113)
1
log_market_cap
0.0007
(0.0010)
2
book_to_market
-0.0005
(0.0031)
3
R-squared
0.0007
4
Observations
600
TipSignificance Stars
The convention for significance stars in finance research:
*** : p < 0.01 (1% level)
** : p < 0.05 (5% level)
* : p < 0.10 (10% level)
Always include a note below your table explaining this convention. Some journals discourage stars and prefer reporting exact p-values or confidence intervals instead.
18.4.3 Comparing Multiple Model Specifications
Research papers typically present several model specifications side-by-side. Let’s create a multi-column regression table:
# Estimate multiple specifications# Model 1: Just market capX1 = sm.add_constant(panel_df[['log_market_cap']])model1 = sm.OLS(panel_df['returns'], X1).fit()# Model 2: Market cap + book-to-marketX2 = sm.add_constant(panel_df[['log_market_cap', 'book_to_market']])model2 = sm.OLS(panel_df['returns'], X2).fit()# Model 3: Add firm fixed effects (simple dummy approach for illustration)panel_df['firm_dummies'] = pd.Categorical(panel_df['firm_id'])X3 = sm.add_constant( pd.concat([ panel_df[['log_market_cap', 'book_to_market']], pd.get_dummies(panel_df['firm_dummies'], drop_first=True, dtype=float) ], axis=1))model3 = sm.OLS(panel_df['returns'], X3).fit()def create_multi_model_table(models, model_names=None):""" Create a regression table comparing multiple models. Parameters ---------- models : list List of fitted statsmodels regression results model_names : list, optional Names for each model column Returns ------- pd.DataFrame Multi-column regression table """if model_names isNone: model_names = [f"Model {i+1}"for i inrange(len(models))]# Collect all unique variables across models all_vars =set()for model in models: all_vars.update(model.params.index)# Remove firm dummies for cleaner display all_vars = [v for v in all_vars ifnotstr(v).startswith('firm_dummies')]# Build table table_dict = {'Variable': []}for name in model_names: table_dict[name] = []for var in all_vars:# Add coefficient row table_dict['Variable'].append(var)for i, model inenumerate(models):if var in model.params.index: coef = model.params[var] p_val = model.pvalues[var]# Format with stars coef_str =f"{coef:.4f}"if p_val <0.01: coef_str +="***"elif p_val <0.05: coef_str +="**"elif p_val <0.10: coef_str +="*" table_dict[model_names[i]].append(coef_str)else: table_dict[model_names[i]].append("")# Add standard error row table_dict['Variable'].append('')for i, model inenumerate(models):if var in model.params.index: se = model.bse[var] table_dict[model_names[i]].append(f"({se:.4f})")else: table_dict[model_names[i]].append("")# Add model statistics stats = [ ('R-squared', lambda m: f"{m.rsquared:.4f}"), ('Adj. R-squared', lambda m: f"{m.rsquared_adj:.4f}"), ('Observations', lambda m: f"{int(m.nobs)}") ]for stat_name, stat_func in stats: table_dict['Variable'].append(stat_name)for i, model inenumerate(models): table_dict[model_names[i]].append(stat_func(model))return pd.DataFrame(table_dict)multi_reg_table = create_multi_model_table([model1, model2, model3])multi_reg_table
Variable
Model 1
Model 2
Model 3
0
2
-0.0064
1
(0.0092)
2
3
0.0032
3
(0.0092)
4
book_to_market
-0.0005
-0.0006
5
(0.0031)
(0.0031)
6
4
0.0005
7
(0.0092)
8
5
-0.0062
9
(0.0092)
10
6
0.0055
11
(0.0092)
12
7
0.0061
13
(0.0092)
14
8
-0.0049
15
(0.0092)
16
9
0.0020
17
(0.0092)
18
10
0.0065
19
(0.0092)
20
const
0.0035
0.0041
0.0030
21
(0.0107)
(0.0113)
(0.0131)
22
log_market_cap
0.0007
0.0007
0.0007
23
(0.0010)
(0.0010)
(0.0011)
24
R-squared
0.0007
0.0007
0.0096
25
Adj. R-squared
-0.0010
-0.0026
-0.0089
26
Observations
600
600
600
18.4.4 Using stargazer for Professional Tables
While our custom functions work well, the stargazer package (Python port of the R package) provides a more automated solution for creating publication-quality regression tables:
from stargazer.stargazer import Stargazer# Create stargazer tablestargazer = Stargazer([model1, model2])# Display as text (can also output to LaTeX)print(stargazer)
<stargazer.stargazer.Stargazer object at 0x133c54ec0>
The stargazer output follows academic conventions closely and includes several useful features automatically.
NoteAlternatives to stargazer
While stargazer is popular, you might also consider:
statsmodels.iolib.summary2: Built into statsmodels for comparing models
linearmodels: Excellent for panel data regressions with sophisticated standard errors
regtabletotext: Lightweight package focused on clean text and LaTeX output
Custom solutions: Building your own functions gives maximum control over formatting
18.4.5 Handling Clustered Standard Errors
In finance research, especially with panel data, you often need to cluster standard errors by firm or time:
# Estimate model with clustered standard errors# Using firm clustersfrom statsmodels.regression.linear_model import OLS# Prepare datay = panel_df['returns']X = sm.add_constant(panel_df[['log_market_cap', 'book_to_market']])# Fit with clustered standard errorsmodel_clustered = OLS(y, X).fit( cov_type='cluster', cov_kwds={'groups': panel_df['firm_id']})print("Standard OLS standard errors:")print(results.bse)print("\nClustered standard errors (by firm):")print(model_clustered.bse)
Standard OLS standard errors:
const 0.011311
log_market_cap 0.001047
book_to_market 0.003072
dtype: float64
Clustered standard errors (by firm):
const 0.008306
log_market_cap 0.000976
book_to_market 0.002056
dtype: float64
WarningClustering and Standard Errors
Clustering typically increases standard errors because it accounts for within-cluster correlation. Key considerations:
Cluster at the level of treatment assignment: If treatment varies by firm, cluster by firm
Few clusters problem: With fewer than 20-30 clusters, consider wild bootstrap
Two-way clustering: For panel data, you may need to cluster by both firm and time
Report clustering in table notes: Always document your clustering approach
Use linearmodels package for more sophisticated clustering options.
18.4.6 Formatting Model Fit Statistics
Different journals have different conventions for which fit statistics to report:
def format_model_stats(results, include_stats=None):""" Format model fit statistics for regression tables. Parameters ---------- results : statsmodels RegressionResults Fitted model include_stats : list, optional Which statistics to include. Options: 'rsq', 'adj_rsq', 'fstat', 'nobs', 'aic', 'bic' Returns ------- pd.DataFrame Formatted statistics """if include_stats isNone: include_stats = ['rsq', 'adj_rsq', 'nobs'] stats_dict = {'rsq': ('R-squared', results.rsquared),'adj_rsq': ('Adj. R-squared', results.rsquared_adj),'fstat': ('F-statistic', results.fvalue),'nobs': ('Observations', results.nobs), }# Add AIC/BIC if availableifhasattr(results, 'aic'): stats_dict['aic'] = ('AIC', results.aic)ifhasattr(results, 'bic'): stats_dict['bic'] = ('BIC', results.bic) formatted = []for key in include_stats:if key in stats_dict: name, value = stats_dict[key]if key =='nobs': formatted.append({'Statistic': name, 'Value': f'{int(value)}'})elif key =='fstat': formatted.append({'Statistic': name, 'Value': f'{value:.2f}'})else: formatted.append({'Statistic': name, 'Value': f'{value:.4f}'})return pd.DataFrame(formatted)model_stats = format_model_stats(results, include_stats=['rsq', 'adj_rsq', 'fstat', 'nobs'])model_stats
Statistic
Value
0
R-squared
0.0007
1
Adj. R-squared
-0.0026
2
F-statistic
0.22
3
Observations
600
18.5 Introduction to LaTeX Tables
Academic journals in finance typically require submissions in LaTeX format. Converting your Python tables to LaTeX is a crucial skill for publishing your research.
18.5.1 Understanding LaTeX Table Structure
A basic LaTeX table has this structure:
\begin{table}[htbp]\centering\caption{Summary Statistics}\label{tab:summary}\begin{tabular}{lcccc}\toprule Variable & Mean & Std Dev & Min & Max \\\midrule Returns & 0.0105 & 0.0543 & -0.1234 & 0.1876 \\ Market Cap & 12.45 & 2.34 & 8.12 & 18.99 \\\bottomrule\end{tabular}\begin{tablenotes}\small\item Note: All values are in decimals.\end{tablenotes}\end{table}
Use consistent precision: Don’t mix 2 and 4 decimal places randomly
Include label: Always add \label{} so you can reference tables in text with \ref{}
In your LaTeX preamble, include:
\usepackage{booktabs}\usepackage{threeparttable}
18.5.4 Creating Regression Tables in LaTeX
For regression tables, we want a specific format showing multiple models side-by-side:
def regression_to_latex(models, model_names, caption, label, dep_var_name='Dependent Variable', notes=None):""" Create publication-quality LaTeX regression table. Parameters ---------- models : list List of fitted statsmodels results model_names : list Names for each model column caption : str Table caption label : str LaTeX label dep_var_name : str Name of dependent variable notes : str, optional Table notes Returns ------- str LaTeX table code """# Collect all variables all_vars =set()for model in models: all_vars.update(model.params.index) all_vars =sorted(list(all_vars))# Start building LaTeX n_models =len(models) col_spec ='l'+'c'* n_models latex =f"""\\begin{{table}}[htbp]\\centering\\caption{{{caption}}}\\label{{{label}}}\\begin{{tabular}}{{{col_spec}}}\\toprule"""# Header row latex +=f" & "+" & ".join([f"({i+1})"for i inrange(n_models)]) +" \\\\\n" latex +=f" & "+" & ".join(model_names) +" \\\\\n" latex +="\\midrule\n"# Variable rowsfor var in all_vars:# Variable name var_display = var.replace('_', '\\_') # Escape underscores for LaTeX latex +=f"{var_display}"# Coefficientsfor model in models:if var in model.params: coef = model.params[var] pval = model.pvalues[var] stars =""if pval <0.01: stars ="^{***}"elif pval <0.05: stars ="^{**}"elif pval <0.10: stars ="^{*}" latex +=f" & ${coef:.4f}{stars}$"else: latex +=" & " latex +=" \\\\\n"# Standard errors latex +=" "for model in models:if var in model.params: se = model.bse[var] latex +=f" & $({se:.4f})$"else: latex +=" & " latex +=" \\\\\n"# Model statistics latex +="\\midrule\n"# R-squared latex +="R-squared"for model in models: latex +=f" & ${model.rsquared:.4f}$" latex +=" \\\\\n"# Observations latex +="Observations"for model in models: latex +=f" & ${int(model.nobs)}$" latex +=" \\\\\n" latex +="\\bottomrule\n" latex +="\\end{tabular}\n"# Add notesif notes: latex +=f"""\\begin{{tablenotes}}\\small\\item \\textit{{Notes:}}{notes}\\end{{tablenotes}}""" latex +="\\end{table}\n"return latex# Generate LaTeX regression tablereg_latex = regression_to_latex( models=[model1, model2], model_names=['Base Model', 'Full Model'], caption='Determinants of Stock Returns', label='tab:regressions', dep_var_name='Returns', notes='This table presents OLS regression results. The dependent variable is monthly stock returns. ''Standard errors are shown in parentheses. ''*, **, and *** denote significance at the 10\\%, 5\\%, and 1\\% levels, respectively.')print(reg_latex)
\begin{table}[htbp]
\centering
\caption{Determinants of Stock Returns}
\label{tab:regressions}
\begin{tabular}{lcc}
\toprule
& (1) & (2) \\
& Base Model & Full Model \\
\midrule
book\_to\_market & & $-0.0005$ \\
& & $(0.0031)$ \\
const & $0.0035$ & $0.0041$ \\
& $(0.0107)$ & $(0.0113)$ \\
log\_market\_cap & $0.0007$ & $0.0007$ \\
& $(0.0010)$ & $(0.0010)$ \\
\midrule
R-squared & $0.0007$ & $0.0007$ \\
Observations & $600$ & $600$ \\
\bottomrule
\end{tabular}
\begin{tablenotes}
\small
\item \textit{Notes:} This table presents OLS regression results. The dependent variable is monthly stock returns. Standard errors are shown in parentheses. *, **, and *** denote significance at the 10\%, 5\%, and 1\% levels, respectively.
\end{tablenotes}
\end{table}
18.5.5 Saving Tables to Files
In practice, you’ll want to save your LaTeX tables to files for inclusion in your paper:
def save_latex_table(latex_string, filename):""" Save LaTeX table to file. Parameters ---------- latex_string : str LaTeX table code filename : str Output filename (should end in .tex) """withopen(filename, 'w') as f: f.write(latex_string)print(f"Table saved to {filename}")# Example (not actually saving to avoid creating files in this tutorial)# save_latex_table(reg_latex, 'tables/regression_results.tex')
In your LaTeX document, you would then include the table with:
\input{tables/regression_results.tex}
NoteWorkflow for Managing Tables
A recommended workflow for research papers:
Generate all tables in Python: Keep table generation code in well-organized scripts
Export to .tex files: Save each table to a separate file in a tables/ directory
Include in main document: Use \input{} to include tables in your paper
Automate updates: When data or specifications change, re-run Python scripts to update all tables
This separation keeps your main LaTeX document clean and makes it easy to update tables as your research evolves.
18.5.6 Using stargazer for LaTeX Output
Returning to stargazer, it can directly generate LaTeX output:
# Create stargazer table with LaTeX outputstargazer_latex = Stargazer([model1, model2])# Configure outputstargazer_latex.title('Determinants of Stock Returns')stargazer_latex.show_model_numbers(True)stargazer_latex.custom_columns(['Base Model', 'Full Model'], [1, 1])# Get LaTeX codeprint(stargazer_latex.render_latex())
stargazer handles many formatting details automatically and is particularly good at managing multiple model specifications.
18.6 Advanced Topics and Best Practices
18.6.1 Panel Data Tables with Fixed Effects
When presenting panel regression results, clearly indicate which fixed effects are included:
# Example of documenting fixed effects in tabledef add_fixed_effects_rows(models, fe_dict):""" Add fixed effects indicator rows to regression table. Parameters ---------- models : list List of model results fe_dict : dict Dictionary mapping model index to dict of FE types Example: {0: {'Firm FE': False, 'Year FE': False}, 1: {'Firm FE': True, 'Year FE': False}} Returns ------- pd.DataFrame Fixed effects indicator table """ fe_types =list(fe_dict[0].keys()) data = []for fe_type in fe_types: row = {'Fixed Effect': fe_type}for i inrange(len(models)): row[f'Model {i+1}'] ='Yes'if fe_dict[i][fe_type] else'No' data.append(row)return pd.DataFrame(data)# Example fixed effects tablefe_indicators = add_fixed_effects_rows( models=[model1, model2, model3], fe_dict={0: {'Firm FE': False, 'Year FE': False},1: {'Firm FE': False, 'Year FE': False},2: {'Firm FE': True, 'Year FE': False} })fe_indicators
Fixed Effect
Model 1
Model 2
Model 3
0
Firm FE
No
No
Yes
1
Year FE
No
No
No
18.6.2 Combining Summary Statistics and Tests
For treatment effect studies, you often want to combine summary statistics with balance tests:
def create_balance_table(df, treatment_col, variables, control_name='Control', treatment_name='Treatment'):""" Create a balance table showing pre-treatment differences. Parameters ---------- df : pd.DataFrame Dataset with treatment indicator treatment_col : str Name of treatment column (binary) variables : list Variables to compare control_name : str Label for control group treatment_name : str Label for treatment group Returns ------- pd.DataFrame Balance table with means and difference tests """ results = []for var in variables: control_data = df[df[treatment_col] ==0][var].dropna() treatment_data = df[df[treatment_col] ==1][var].dropna() control_mean = control_data.mean() treatment_mean = treatment_data.mean() t_stat, p_val = scipy_stats.ttest_ind(control_data, treatment_data) results.append({'Variable': var,f'{control_name} Mean': f'{control_mean:.3f}',f'{treatment_name} Mean': f'{treatment_mean:.3f}','Difference': f'{treatment_mean - control_mean:.3f}','t-statistic': f'{t_stat:.3f}','p-value': f'{p_val:.3f}' })return pd.DataFrame(results)# Example (creating artificial treatment variable)panel_df['treatment'] = (panel_df['firm_id'] <=5).astype(int)balance_table = create_balance_table( panel_df,'treatment', ['returns', 'log_market_cap', 'book_to_market'], control_name='Control Firms', treatment_name='Treatment Firms')balance_table
Variable
Control Firms Mean
Treatment Firms Mean
Difference
t-statistic
p-value
0
returns
0.013
0.008
-0.005
1.178
0.239
1
log_market_cap
10.054
10.078
0.024
-0.150
0.880
2
book_to_market
0.986
0.987
0.002
-0.029
0.977
18.6.3 Journal-Specific Formatting
Different journals have different table formatting requirements. Here are some common variations:
# Example: Compact format vs. spacious formatdef format_for_journal(df, journal_style='compact'):""" Format table according to journal requirements. Parameters ---------- df : pd.DataFrame Table to format journal_style : str 'compact' for journals with page limits, 'spacious' for others Returns ------- str Formatted LaTeX code """if journal_style =='compact':# Use smaller font, tighter spacing latex = df.to_latex(index=True, escape=False) latex ="\\small\n"+ latex latex = latex.replace("\\begin{tabular}","\\begin{tabular}[t]")else:# Standard format with more spacing latex = df.to_latex(index=True, escape=False)return latex# Compact version for journals with strict page limitscompact_table = format_for_journal(formatted_stats, journal_style='compact')print("Compact format:")print(compact_table[:300]) # Show first 300 characters