analysis/analyze.py (149 lines of code) (raw):
import pandas as pd
import altair as alt
import copy
import numpy as np
#
# Data transformation and Aggregation functions
#
def normalizeDatBySize(dat, normalize_by=None, sqrt=True, inplace=False):
if not inplace:
dat = copy.deepcopy(dat)
if normalize_by is None:
return dat
if sqrt:
dat.error *= np.sqrt(dat[normalize_by])
else:
dat.error *= dat[normalize_by]
return dat
def cleanByClause(by):
by = [x for x in by if x != alt.Undefined and x is not None]
return by
def populationVar(x):
return np.var(x, ddof=0)
def rawAggregateExperimentResults(dat, by=['qid', 'query_idx', 'query', 'sketch_name']):
by = cleanByClause(by)
aggdat = dat.groupby(by, as_index=False).agg(
bias=('error', np.mean),
std=('error', np.std),
rmse=('error', lambda x: np.linalg.norm(x)),
q5=('error',lambda x: np.quantile(x, 0.05)),
q25=('error',lambda x: np.quantile(x, 0.25)),
q75=('error',lambda x: np.quantile(x, 0.75)),
q95=('error',lambda x: np.quantile(x, 0.95)),
size=('sketch_size', np.mean),
size_bytes=('sketch_bytes', np.mean),
observed_max_size=('sketch_size', np.max),
count=('error', 'count'),
)
# two level calculation for variance
vardat_by_workload = dat.groupby(by + ['data_seed'], as_index=False).agg(
var=('error', populationVar),
bias=('error', np.mean),
count=('error','count'),
)
vardat_by_workload['var'] = vardat_by_workload['var'] / np.sqrt(vardat_by_workload['count']-1 + 1e-6)
vardat = vardat_by_workload.groupby(by, as_index=False).agg(
var_expectation=('bias', populationVar),
expected_var=('var', np.mean),
count_by_seed=('bias', 'count')
)
vardat['var_bias'] = (vardat['var_expectation'] + vardat['expected_var']) / (vardat['count_by_seed']-1 + 1e-6)
aggdat = aggdat.merge(vardat)
aggdat['bias_lower95'] = aggdat['bias'] - 1.96 * np.sqrt(aggdat['var_bias'])
aggdat['bias_upper95'] = aggdat['bias'] + 1.96 * np.sqrt(aggdat['var_bias'])
return aggdat
def meltRawAggregateExperimentResults(aggdat, by=['qid', 'query_idx', 'query', 'sketch_name']):
by = cleanByClause(by)
return aggdat.melt(id_vars=by, value_vars=['bias', 'std', 'q5', 'q25','q75', 'q95', 'size', 'size_bytes',
'bias_lower95', 'bias_upper95'])
def aggregateExperimentResults(dat, by=['qid', 'query_idx', 'query', 'sketch_name']):
aggdat = rawAggregateExperimentResults(dat, by=by)
return meltRawAggregateExperimentResults(aggdat, by=by)
########################################################################################################
# Filter the melted dataset to pick out the stats useful for each plotting task
def filterErrorStats(aggdat):
return aggdat[aggdat.variable.isin(['bias', 'std', 'q5', 'q25','q75', 'q95'])]
def filterBias(dat):
return dat[dat.variable == 'bias']
def filterBiasCI(dat):
return dat[dat.variable.isin(['bias_lower95', 'bias_upper95'])]
########################################################################################################
color_coding_dict = {
'bias': 'black',
# 'std': None,
'q5': 'red',
'q95': 'red',
'q25': 'orange',
'q75': 'orange'
}
########################################################################################################
#
# Plotting functions
#
def makePlotLabels(column=alt.Undefined, row=alt.Undefined,
normalize_by=None, normalize_by_sqrt=False,
base_title="Error"):
# make title
if column != alt.Undefined and row != alt.Undefined:
title_group = f" by {column} x {row}"
elif column != alt.Undefined:
title_group = f" by {column}"
elif row != alt.Undefined:
title_group = f" by {row}"
else:
title_group = ""
title = f"{base_title}{title_group}"
if normalize_by is None:
yaxis = 'Error'
elif normalize_by_sqrt:
yaxis = 'Error * sqrt(size)'
else:
yaxis = 'Error * size'
return title, yaxis
def makeListLike(dat, unit):
if pd.api.types.is_list_like(unit):
return unit
else:
return [unit]
def plotExperimentResults(dat, x='query_idx', unit='qid',
column=alt.Undefined, row=alt.Undefined,
normalize_by=None, normalize_by_sqrt=False,
base_title="Error"):
unit = makeListLike(unit)
dat = normalizeDatBySize(dat, normalize_by=normalize_by, sqrt=normalize_by_sqrt)
aggdat = aggregateExperimentResults(dat, by=[x, column, row] + unit)
title, yaxis = makePlotLabels(column=column, row=row, normalize_by=normalize_by, normalize_by_sqrt=normalize_by_sqrt)
# make actual plot
chart = alt.Chart(filterErrorStats(aggdat)).mark_line().encode(
x=x,
y=alt.Y('value', axis=alt.Axis(title=yaxis)),
color=alt.Color('variable', scale=alt.Scale(domain=list(color_coding_dict.keys()), range=list(color_coding_dict.values()))),
#)'color_coding'),
detail="variable"
).facet(column=column, row=row).properties(
title=title
)
return (chart, aggdat)
def plotBias(dat, x='query_idx', unit='qid', column=alt.Undefined, row=alt.Undefined, normalize_by=None, normalize_by_sqrt=False):
unit = makeListLike(unit)
raw_aggdat = rawAggregateExperimentResults(dat, by=[x, column, row] + unit)
title, yaxis = makePlotLabels(column=column, row=row, normalize_by=normalize_by, normalize_by_sqrt=normalize_by_sqrt, base_title="Bias")
chart = alt.Chart().mark_line().encode(
x=f"{x}:Q",
y=alt.Y('bias:Q', title="bias"),
)
error_bars = alt.Chart().mark_area(opacity=0.3).encode(
x=f"{x}:Q",
y=alt.Y('bias_lower95:Q', title="bias"),
y2='bias_upper95:Q',
)
combined_chart = alt.layer(chart, error_bars, data = raw_aggdat).facet(
column=column, row=row
).properties(
title=title
)
return (combined_chart, raw_aggdat)
def plotErrorVsSize(dat, x='sketch_bytes', unit='qid'):
unit = makeListLike(unit)
raw_aggdat = rawAggregateExperimentResults(dat, by=[x, 'sketch_name'] + unit)
print(raw_aggdat.shape)
chart = alt.Chart(raw_aggdat).mark_point(size=10, opacity=0.3).encode(
x=f"{x}:Q",
y=alt.Y('rmse:Q', title="rmse"),
color=alt.Color('sketch_name'),
)
chart = chart + chart.transform_loess(f'{x}', f'rmse', groupby=['sketch_name']).mark_line(size=4)
return chart, raw_aggdat
def plotSizeVsParams(dat, x, size="sketch_size"):
chart = alt.Chart(dat).mark_point(size=10, opacity=0.3).encode(
x=f"{x}:Q",
y=alt.Y(f'{size}:Q', title=size),
color=alt.Color('sketch_name'),
)
chart = chart + chart.transform_loess(f'{x}', f'{size}', groupby=['sketch_name']).mark_line(size=4)
return chart