#!/usr/bin/env python3 # # Use pandas + bokeh to create graphs/charts/plots for stats CSV (to_csv.py). # import os import pandas as pd from bokeh.io import curdoc from bokeh.models import ColumnDataSource, HoverTool from bokeh.plotting import figure from bokeh.palettes import Dark2_5 as palette from bokeh.models.formatters import DatetimeTickFormatter import pandas_bokeh import argparse import itertools from fnmatch import fnmatch datecolumn = '0time' if __name__ == '__main__': parser = argparse.ArgumentParser(description='Graph CSV files') parser.add_argument('infiles', nargs='+', type=str, help='CSV files to plot.') parser.add_argument('--cols', type=str, help='Columns to plot (CSV list)') parser.add_argument('--skip', type=str, help='Columns to skip (CSV list)') parser.add_argument('--group-by', type=str, help='Group data series by field') parser.add_argument('--chart-cols', type=int, default=3, help='Number of chart columns') parser.add_argument('--plot-width', type=int, default=400, help='Per-plot width') parser.add_argument('--plot-height', type=int, default=300, help='Per-plot height') parser.add_argument('--out', type=str, default='out.html', help='Output file (HTML)') args = parser.parse_args() outpath = args.out if args.cols is None: cols = None else: cols = args.cols.split(',') cols.append(datecolumn) if args.skip is None: assert cols is None, "--cols and --skip are mutually exclusive" skip = None else: skip = args.skip.split(',') group_by = args.group_by pandas_bokeh.output_file(outpath) curdoc().theme = 'dark_minimal' figs = {} plots = [] for infile in args.infiles: colors = itertools.cycle(palette) cols_to_use = cols if skip is not None: # First read available fields avail_cols = list(pd.read_csv(infile, nrows=1)) cols_to_use = [c for c in avail_cols if len([x for x in skip if fnmatch(c, x)]) == 0] df = pd.read_csv(infile, parse_dates=[datecolumn], index_col=datecolumn, usecols=cols_to_use) title = os.path.basename(infile) print(f"{infile}:") if group_by is not None: grp = df.groupby([group_by]) # Make one plot per column, skipping the index and group_by cols. for col in df.keys(): if col in (datecolumn, group_by): continue print("col: ", col) for _, dg in grp: print(col, " dg:\n", dg.head()) figtitle = f"{title}: {col}" p = figs.get(figtitle, None) if p is None: p = figure(title=f"{title}: {col}", plot_width=args.plot_width, plot_height=args.plot_height, x_axis_type='datetime', tools="hover,box_zoom,wheel_zoom," + "reset,pan,poly_select,tap,save") figs[figtitle] = p plots.append(p) p.add_tools(HoverTool( tooltips=[ ("index", "$index"), ("time", "@0time{%F}"), ("y", "$y"), ("desc", "$name"), ], formatters={ "@0time": "datetime", }, mode='vline')) p.xaxis.formatter = DatetimeTickFormatter( minutes=['%H:%M'], seconds=['%H:%M:%S']) source = ColumnDataSource(dg) val = dg[group_by][0] for k in dg: if k != col: continue p.line(x=datecolumn, y=k, source=source, legend_label=f"{k}[{val}]", name=f"{k}[{val}]", color=next(colors)) continue else: p = df.plot_bokeh(title=title, kind='line', show_figure=False) plots.append(p) for p in plots: p.legend.click_policy = "hide" grid = [] for i in range(0, len(plots), args.chart_cols): grid.append(plots[i:i + args.chart_cols]) pandas_bokeh.plot_grid(grid)