123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- #
- # Copyright (C) 2013 The Android Open Source Project
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- #
- """Histogram generation tools."""
- from collections import defaultdict
- from update_payload import format_utils
- class Histogram(object):
- """A histogram generating object.
- This object serves the sole purpose of formatting (key, val) pairs as an
- ASCII histogram, including bars and percentage markers, and taking care of
- label alignment, scaling, etc. In addition to the standard __init__
- interface, two static methods are provided for conveniently converting data
- in different formats into a histogram. Histogram generation is exported via
- its __str__ method, and looks as follows:
- Yes |################ | 5 (83.3%)
- No |### | 1 (16.6%)
- TODO(garnold) we may want to add actual methods for adding data or tweaking
- the output layout and formatting. For now, though, this is fine.
- """
- def __init__(self, data, scale=20, formatter=None):
- """Initialize a histogram object.
- Args:
- data: list of (key, count) pairs constituting the histogram
- scale: number of characters used to indicate 100%
- formatter: function used for formatting raw histogram values
- """
- self.data = data
- self.scale = scale
- self.formatter = formatter or str
- self.max_key_len = max([len(str(key)) for key, count in self.data])
- self.total = sum([count for key, count in self.data])
- @staticmethod
- def FromCountDict(count_dict, scale=20, formatter=None, key_names=None):
- """Takes a dictionary of counts and returns a histogram object.
- This simply converts a mapping from names to counts into a list of (key,
- count) pairs, optionally translating keys into name strings, then
- generating and returning a histogram for them. This is a useful convenience
- call for clients that update a dictionary of counters as they (say) scan a
- data stream.
- Args:
- count_dict: dictionary mapping keys to occurrence counts
- scale: number of characters used to indicate 100%
- formatter: function used for formatting raw histogram values
- key_names: dictionary mapping keys to name strings
- Returns:
- A histogram object based on the given data.
- """
- namer = None
- if key_names:
- namer = lambda key: key_names[key]
- else:
- namer = lambda key: key
- hist = [(namer(key), count) for key, count in count_dict.items()]
- return Histogram(hist, scale, formatter)
- @staticmethod
- def FromKeyList(key_list, scale=20, formatter=None, key_names=None):
- """Takes a list of (possibly recurring) keys and returns a histogram object.
- This converts the list into a dictionary of counters, then uses
- FromCountDict() to generate the actual histogram. For example:
- ['a', 'a', 'b', 'a', 'b'] --> {'a': 3, 'b': 2} --> ...
- Args:
- key_list: list of (possibly recurring) keys
- scale: number of characters used to indicate 100%
- formatter: function used for formatting raw histogram values
- key_names: dictionary mapping keys to name strings
- Returns:
- A histogram object based on the given data.
- """
- count_dict = defaultdict(int) # Unset items default to zero
- for key in key_list:
- count_dict[key] += 1
- return Histogram.FromCountDict(count_dict, scale, formatter, key_names)
- def __str__(self):
- hist_lines = []
- hist_bar = '|'
- for key, count in self.data:
- if self.total:
- bar_len = count * self.scale / self.total
- hist_bar = '|%s|' % ('#' * bar_len).ljust(self.scale)
- line = '%s %s %s' % (
- str(key).ljust(self.max_key_len),
- hist_bar,
- self.formatter(count))
- percent_str = format_utils.NumToPercent(count, self.total)
- if percent_str:
- line += ' (%s)' % percent_str
- hist_lines.append(line)
- return '\n'.join(hist_lines)
- def GetKeys(self):
- """Returns the keys of the histogram."""
- return [key for key, _ in self.data]
|