1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
#!/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)
|