Files
OpenCellular/extra/usb_power/stats_manager.py
Mengqi Guo aaf0169b11 sweetberry: print MEAN in json format
This CL adds the flag --save_stats_json which prints the MEAN
of sweetberry readings into json format. Corresponding unit
test has been added too. Printing just the MEAN in json format
makes it easier to read it back in power_telemetry_logger,
which uploads the stats to the dashboard.

This CL is part of the effort to start sweetberry measurements
while running power autotests.

BRANCH=None
BUG=b:68956240
TEST=./powerlog.py -b xxx.board -c xxx.scenario \
--save_stats_json [directory to store it]
python -m  unittest -v stats_manager_unittest

Change-Id: I8274bcac21175f6c53184ced79b6ffe5e7d7a72a
Signed-off-by: Mengqi Guo <mqg@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/807636
Reviewed-by: Nick Sanders <nsanders@chromium.org>
Reviewed-by: Puthikorn Voravootivat <puthik@chromium.org>
Reviewed-by: Todd Broch <tbroch@chromium.org>
2017-12-05 22:43:45 -08:00

157 lines
4.8 KiB
Python

# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Calculates statistics for lists of data and pretty print them."""
from __future__ import print_function
import collections
import json
import numpy
import os
STATS_PREFIX = '@@'
KEY_PREFIX = '__'
# This prefix is used for keys that should not be shown in the summary tab, such
# as timeline keys.
NOSHOW_PREFIX = '!!'
class StatsManager(object):
"""Calculates statistics for several lists of data(float)."""
def __init__(self):
"""Initialize infrastructure for data and their statistics."""
self._data = collections.defaultdict(list)
self._summary = {}
def AddValue(self, domain, value):
"""Add one value for a domain.
Args:
domain: the domain name for the value.
value: one time reading for domain, expect type float.
"""
if isinstance(value, int):
value = float(value)
if isinstance(value, float):
self._data[domain].append(value)
return
print('Warning: value %s for domain %s is not a number, thus ignored.' %
(value, domain))
def CalculateStats(self):
"""Calculate stats for all domain-data pairs.
First erases all previous stats, then calculate stats for all data.
"""
self._summary = {}
for domain, data in self._data.iteritems():
data_np = numpy.array(data)
self._summary[domain] = {
'mean' : data_np.mean(),
'min' : data_np.min(),
'max' : data_np.max(),
'stddev' : data_np.std(),
'count' : data_np.size,
}
def _SummaryToString(self, prefix=STATS_PREFIX):
"""Format summary into a string, ready for pretty print.
Args:
prefix: start every row in summary string with prefix, for easier reading.
"""
headers = ('NAME', 'COUNT', 'MEAN', 'STDDEV', 'MAX', 'MIN')
table = [headers]
for domain in sorted(self._summary.keys()):
if domain.startswith(NOSHOW_PREFIX):
continue
stats = self._summary[domain]
row = [domain.lstrip(KEY_PREFIX)]
row.append(str(stats['count']))
for entry in headers[2:]:
row.append('%.2f' % stats[entry.lower()])
table.append(row)
max_col_width = []
for col_idx in range(len(table[0])):
col_item_widths = [len(row[col_idx]) for row in table]
max_col_width.append(max(col_item_widths))
formatted_table = []
for row in table:
formatted_row = prefix + ' '
for i in range(len(row)):
formatted_row += row[i].rjust(max_col_width[i] + 2)
formatted_table.append(formatted_row)
return '\n'.join(formatted_table)
def PrintSummary(self, prefix=STATS_PREFIX):
"""Print the formatted summary.
Args:
prefix: start every row in summary string with prefix, for easier reading.
"""
summary_str = self._SummaryToString(prefix=prefix)
print(summary_str)
def GetSummary(self):
"""Getter for summary."""
return self._summary
def SaveSummary(self, directory, fname='summary.txt', prefix=STATS_PREFIX):
"""Save summary to file.
Args:
directory: directory to save the summary in.
fname: filename to save summary under.
prefix: start every row in summary string with prefix, for easier reading.
"""
summary_str = self._SummaryToString(prefix=prefix) + '\n'
if not os.path.exists(directory):
os.makedirs(directory)
fname = os.path.join(directory, fname)
with open(fname, 'w') as f:
f.write(summary_str)
def SaveSummaryJSON(self, directory, fname='summary.json'):
"""Save summary (only MEAN) into a JSON file.
Args:
directory: directory to save the JSON summary in.
fname: filename to save summary under.
"""
data = {
domain: self._summary[domain]['mean']
for domain in sorted(self._summary.keys())
if not domain.startswith(NOSHOW_PREFIX)
}
if not os.path.exists(directory):
os.makedirs(directory)
fname = os.path.join(directory, fname)
with open(fname, 'w') as f:
json.dump(data, f)
def GetRawData(self):
"""Getter for all raw_data."""
return self._data
def SaveRawData(self, directory, dirname='raw_data'):
"""Save raw data to file.
Args:
directory: directory to create the raw data folder in.
dirname: folder in which raw data live.
"""
if not os.path.exists(directory):
os.makedirs(directory)
dirname = os.path.join(directory, dirname)
if not os.path.exists(dirname):
os.makedirs(dirname)
for domain, data in self._data.iteritems():
fname = domain + '.txt'
fname = os.path.join(dirname, fname)
with open(fname, 'w') as f:
f.write('\n'.join('%.2f' % value for value in data) + '\n')