diff --git a/copulas/sdmetrics.py b/copulas/sdmetrics.py
deleted file mode 100644
index d9090f71..00000000
--- a/copulas/sdmetrics.py
+++ /dev/null
@@ -1,479 +0,0 @@
-"""Visualization methods for SDMetrics."""
-
-import pandas as pd
-import plotly.express as px
-import plotly.figure_factory as ff
-from pandas.api.types import is_datetime64_dtype
-
-from copulas.utils import get_missing_percentage, is_datetime
-from copulas.utils2 import PlotConfig
-
-
-def _generate_column_bar_plot(real_data, synthetic_data, plot_kwargs={}):
- """Generate a bar plot of the real and synthetic data.
-
- Args:
- real_column (pandas.Series):
- The real data for the desired column.
- synthetic_column (pandas.Series):
- The synthetic data for the desired column.
- plot_kwargs (dict, optional):
- Dictionary of keyword arguments to pass to px.histogram. Keyword arguments
- provided this way will overwrite defaults.
-
- Returns:
- plotly.graph_objects._figure.Figure
- """
- all_data = pd.concat([real_data, synthetic_data], axis=0, ignore_index=True)
- histogram_kwargs = {
- 'x': 'values',
- 'barmode': 'group',
- 'color_discrete_sequence': [PlotConfig.DATACEBO_DARK, PlotConfig.DATACEBO_GREEN],
- 'pattern_shape': 'Data',
- 'pattern_shape_sequence': ['', '/'],
- 'histnorm': 'probability density',
- }
- histogram_kwargs.update(plot_kwargs)
- fig = px.histogram(
- all_data,
- **histogram_kwargs
- )
-
- return fig
-
-
-def _generate_heatmap_plot(all_data, columns):
- """Generate heatmap plot for discrete data.
-
- Args:
- all_data (pandas.DataFrame):
- The real and synthetic data for the desired column pair containing a
- ``Data`` column that indicates whether is real or synthetic.
- columns (list):
- A list of the columns being plotted.
-
- Returns:
- plotly.graph_objects._figure.Figure
- """
- fig = px.density_heatmap(
- all_data,
- x=columns[0],
- y=columns[1],
- facet_col='Data',
- histnorm='probability'
- )
-
- fig.update_layout(
- title_text=f"Real vs Synthetic Data for columns '{columns[0]}' and '{columns[1]}'",
- coloraxis={'colorscale': [PlotConfig.DATACEBO_DARK, PlotConfig.DATACEBO_GREEN]},
- font={'size': PlotConfig.FONT_SIZE},
- )
-
- fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1] + ' Data'))
-
- return fig
-
-
-def _generate_box_plot(all_data, columns):
- """Generate a box plot for mixed discrete and continuous column data.
-
- Args:
- all_data (pandas.DataFrame):
- The real and synthetic data for the desired column pair containing a
- ``Data`` column that indicates whether is real or synthetic.
- columns (list):
- A list of the columns being plotted.
-
- Returns:
- plotly.graph_objects._figure.Figure
- """
- fig = px.box(
- all_data,
- x=columns[0],
- y=columns[1],
- color='Data',
- color_discrete_map={
- 'Real': PlotConfig.DATACEBO_DARK,
- 'Synthetic': PlotConfig.DATACEBO_GREEN
- },
- )
-
- fig.update_layout(
- title=f"Real vs. Synthetic Data for columns '{columns[0]}' and '{columns[1]}'",
- plot_bgcolor=PlotConfig.BACKGROUND_COLOR,
- font={'size': PlotConfig.FONT_SIZE},
- )
-
- return fig
-
-
-def _generate_scatter_plot(all_data, columns):
- """Generate a scatter plot for column pair plot.
-
- Args:
- all_data (pandas.DataFrame):
- The real and synthetic data for the desired column pair containing a
- ``Data`` column that indicates whether is real or synthetic.
- columns (list):
- A list of the columns being plotted.
-
- Returns:
- plotly.graph_objects._figure.Figure
- """
- fig = px.scatter(
- all_data,
- x=columns[0],
- y=columns[1],
- color='Data',
- color_discrete_map={
- 'Real': PlotConfig.DATACEBO_DARK,
- 'Synthetic': PlotConfig.DATACEBO_GREEN
- },
- symbol='Data'
- )
-
- fig.update_layout(
- title=f"Real vs. Synthetic Data for columns '{columns[0]}' and '{columns[1]}'",
- plot_bgcolor=PlotConfig.BACKGROUND_COLOR,
- font={'size': PlotConfig.FONT_SIZE},
- )
-
- return fig
-
-
-def _generate_column_distplot(real_data, synthetic_data, plot_kwargs={}):
- """Plot the real and synthetic data as a distplot.
-
- Args:
- real_data (pandas.DataFrame):
- The real data for the desired column.
- synthetic_data (pandas.DataFrame):
- The synthetic data for the desired column.
- plot_kwargs (dict, optional):
- Dictionary of keyword arguments to pass to px.histogram. Keyword arguments
- provided this way will overwrite defaults.
-
- Returns:
- plotly.graph_objects._figure.Figure
- """
- default_distplot_kwargs = {
- 'show_hist': False,
- 'show_rug': False,
- 'colors': [PlotConfig.DATACEBO_DARK, PlotConfig.DATACEBO_GREEN]
- }
-
- fig = ff.create_distplot(
- [real_data['values'], synthetic_data['values']],
- ['Real', 'Synthetic'],
- **{**default_distplot_kwargs, **plot_kwargs}
- )
-
- return fig
-
-
-def _generate_column_plot(real_column,
- synthetic_column,
- plot_type,
- plot_kwargs={},
- plot_title=None,
- x_label=None):
- """Generate a plot of the real and synthetic data.
-
- Args:
- real_column (pandas.Series):
- The real data for the desired column.
- synthetic_column (pandas.Series):
- The synthetic data for the desired column.
- plot_type (str):
- The type of plot to use. Must be one of 'bar' or 'distplot'.
- hist_kwargs (dict, optional):
- Dictionary of keyword arguments to pass to px.histogram. Keyword arguments
- provided this way will overwrite defaults.
- plot_title (str, optional):
- Title to use for the plot. Defaults to 'Real vs. Synthetic Data for column {column}'
- x_label (str, optional):
- Label to use for x-axis. Defaults to 'Category'.
-
- Returns:
- plotly.graph_objects._figure.Figure
- """
- if plot_type not in ['bar', 'distplot']:
- raise ValueError(
- "Unrecognized plot_type '{plot_type}'. Pleas use one of 'bar' or 'distplot'"
- )
-
- column_name = real_column.name if hasattr(real_column, 'name') else ''
-
- missing_data_real = get_missing_percentage(real_column)
- missing_data_synthetic = get_missing_percentage(synthetic_column)
-
- real_data = pd.DataFrame({'values': real_column.copy().dropna()})
- real_data['Data'] = 'Real'
- synthetic_data = pd.DataFrame({'values': synthetic_column.copy().dropna()})
- synthetic_data['Data'] = 'Synthetic'
-
- is_datetime_sdtype = False
- if is_datetime64_dtype(real_column.dtype):
- is_datetime_sdtype = True
- real_data['values'] = real_data['values'].astype('int64')
- synthetic_data['values'] = synthetic_data['values'].astype('int64')
-
- trace_args = {}
-
- if plot_type == 'bar':
- fig = _generate_column_bar_plot(real_data, synthetic_data, plot_kwargs)
- elif plot_type == 'distplot':
- x_label = x_label or 'Value'
- fig = _generate_column_distplot(real_data, synthetic_data, plot_kwargs)
- trace_args = {'fill': 'tozeroy'}
-
- for i, name in enumerate(['Real', 'Synthetic']):
- fig.update_traces(
- x=pd.to_datetime(fig.data[i].x) if is_datetime_sdtype else fig.data[i].x,
- hovertemplate=f'{name}
Frequency: %{{y}}',
- selector={'name': name},
- **trace_args
- )
-
- show_missing_values = missing_data_real > 0 or missing_data_synthetic > 0
- annotations = [] if not show_missing_values else [
- {
- 'xref': 'paper',
- 'yref': 'paper',
- 'x': 1.0,
- 'y': 1.05,
- 'showarrow': False,
- 'text': (
- f'*Missing Values: Real Data ({missing_data_real}%), '
- f'Synthetic Data ({missing_data_synthetic}%)'
- ),
- },
- ]
-
- if not plot_title:
- plot_title = f"Real vs. Synthetic Data for column '{column_name}'"
-
- if not x_label:
- x_label = 'Category'
-
- fig.update_layout(
- title=plot_title,
- xaxis_title=x_label,
- yaxis_title='Frequency',
- plot_bgcolor=PlotConfig.BACKGROUND_COLOR,
- annotations=annotations,
- font={'size': PlotConfig.FONT_SIZE},
- )
- return fig
-
-
-def _generate_cardinality_plot(real_data,
- synthetic_data,
- parent_primary_key,
- child_foreign_key,
- plot_type='bar'):
- plot_title = (
- f"Relationship (child foreign key='{child_foreign_key}' and parent "
- f"primary key='{parent_primary_key}')"
- )
- x_label = '# of Children (per Parent)'
-
- plot_kwargs = {}
- if plot_type == 'bar':
- max_cardinality = max(max(real_data), max(synthetic_data))
- min_cardinality = min(min(real_data), min(synthetic_data))
- plot_kwargs = {
- 'nbins': max_cardinality - min_cardinality + 1
- }
-
- return _generate_column_plot(real_data, synthetic_data, plot_type,
- plot_kwargs, plot_title, x_label)
-
-
-def _get_cardinality(parent_table, child_table, parent_primary_key, child_foreign_key):
- """Return the cardinality of the parent-child relationship.
-
- Args:
- parent_table (pandas.DataFrame):
- The parent table.
- child_table (pandas.DataFrame):
- The child table.
- parent_primary_key (string):
- The name of the primary key column in the parent table.
- child_foreign_key (string):
- The name of the foreign key column in the child table.
-
- Returns:
- pandas.DataFrame
- """
- child_counts = child_table[child_foreign_key].value_counts().rename('# children')
- cardinalities = child_counts.reindex(parent_table[parent_primary_key], fill_value=0).to_frame()
-
- return cardinalities.sort_values('# children')['# children']
-
-
-def get_cardinality_plot(real_data, synthetic_data, child_table_name, parent_table_name,
- child_foreign_key, parent_primary_key, plot_type='bar'):
- """Return a plot of the cardinality of the parent-child relationship.
-
- Args:
- real_data (dict):
- The real data.
- synthetic_data (dict):
- The synthetic data.
- child_table_name (string):
- The name of the child table.
- parent_table_name (string):
- The name of the parent table.
- child_foreign_key (string):
- The name of the foreign key column in the child table.
- parent_primary_key (string):
- The name of the primary key column in the parent table.
- plot_type (string, optional):
- The plot type to use to plot the cardinality. Must be either 'bar' or 'distplot'.
- Defaults to 'bar'.
-
- Returns:
- plotly.graph_objects._figure.Figure
- """
- if plot_type not in ['bar', 'distplot']:
- raise ValueError(
- f"Invalid plot_type '{plot_type}'. Please use one of ['bar', 'distplot'].")
-
- real_cardinality = _get_cardinality(
- real_data[parent_table_name], real_data[child_table_name],
- parent_primary_key, child_foreign_key
- )
- synth_cardinality = _get_cardinality(
- synthetic_data[parent_table_name],
- synthetic_data[child_table_name],
- parent_primary_key, child_foreign_key
- )
-
- fig = _generate_cardinality_plot(
- real_cardinality,
- synth_cardinality,
- parent_primary_key,
- child_foreign_key,
- plot_type=plot_type
- )
-
- return fig
-
-
-def get_column_plot(real_data, synthetic_data, column_name, plot_type=None):
- """Return a plot of the real and synthetic data for a given column.
-
- Args:
- real_data (pandas.DataFrame):
- The real table data.
- synthetic_data (pandas.DataFrame):
- The synthetic table data.
- column_name (str):
- The name of the column.
- plot_type (str or None):
- The plot to be used. Can choose between ``distplot``, ``bar`` or ``None``. If ``None`
- select between ``distplot`` or ``bar`` depending on the data that the column contains,
- ``distplot`` for datetime and numerical values and ``bar`` for categorical.
- Defaults to ``None``.
-
- Returns:
- plotly.graph_objects._figure.Figure
- """
- if plot_type not in ['bar', 'distplot', None]:
- raise ValueError(
- f"Invalid plot_type '{plot_type}'. Please use one of ['bar', 'distplot', None]."
- )
-
- if column_name not in real_data.columns:
- raise ValueError(f"Column '{column_name}' not found in real table data.")
- if column_name not in synthetic_data.columns:
- raise ValueError(f"Column '{column_name}' not found in synthetic table data.")
-
- real_column = real_data[column_name]
- if plot_type is None:
- column_is_datetime = is_datetime(real_data[column_name])
- dtype = real_column.dropna().infer_objects().dtype.kind
- if column_is_datetime or dtype in ('i', 'f'):
- plot_type = 'distplot'
- else:
- plot_type = 'bar'
-
- real_column = real_data[column_name]
- synthetic_column = synthetic_data[column_name]
-
- fig = _generate_column_plot(real_column, synthetic_column, plot_type)
-
- return fig
-
-
-def get_column_pair_plot(real_data, synthetic_data, column_names, plot_type=None):
- """Return a plot of the real and synthetic data for a given column pair.
-
- Args:
- real_data (pandas.DataFrame):
- The real table data.
- synthetic_column (pandas.Dataframe):
- The synthetic table data.
- column_names (list[string]):
- The names of the two columns to plot.
- plot_type (str or None):
- The plot to be used. Can choose between ``box``, ``heatmap``, ``scatter`` or ``None``.
- If ``None` select between ``box``, ``heatmap`` or ``scatter`` depending on the data
- that the column contains, ``scatter`` used for datetime and numerical values,
- ``heatmap`` for categorical and ``box`` for a mix of both. Defaults to ``None``.
-
- Returns:
- plotly.graph_objects._figure.Figure
- """
- if len(column_names) != 2:
- raise ValueError('Must provide exactly two column names.')
-
- if not set(column_names).issubset(real_data.columns):
- raise ValueError(
- f'Missing column(s) {set(column_names) - set(real_data.columns)} in real data.'
- )
-
- if not set(column_names).issubset(synthetic_data.columns):
- raise ValueError(
- f'Missing column(s) {set(column_names) - set(synthetic_data.columns)} '
- 'in synthetic data.'
- )
-
- if plot_type not in ['box', 'heatmap', 'scatter', None]:
- raise ValueError(
- f"Invalid plot_type '{plot_type}'. Please use one of "
- "['box', 'heatmap', 'scatter', None]."
- )
-
- real_data = real_data[column_names]
- synthetic_data = synthetic_data[column_names]
- if plot_type is None:
- plot_type = []
- for column_name in column_names:
- column = real_data[column_name]
- dtype = column.dropna().infer_objects().dtype.kind
- if dtype in ('i', 'f') or is_datetime(column):
- plot_type.append('scatter')
- else:
- plot_type.append('heatmap')
-
- if len(set(plot_type)) > 1:
- plot_type = 'box'
- else:
- plot_type = plot_type.pop()
-
- # Merge the real and synthetic data and add a flag ``Data`` to indicate each one.
- columns = list(real_data.columns)
- real_data = real_data.copy()
- real_data['Data'] = 'Real'
- synthetic_data = synthetic_data.copy()
- synthetic_data['Data'] = 'Synthetic'
- all_data = pd.concat([real_data, synthetic_data], axis=0, ignore_index=True)
-
- if plot_type == 'scatter':
- return _generate_scatter_plot(all_data, columns)
- elif plot_type == 'heatmap':
- return _generate_heatmap_plot(all_data, columns)
-
- return _generate_box_plot(all_data, columns)