scripts: plot[mpl].py: Adopted -s/--sort and -S for legend sorting
Before this, the only option for ordering the legend was by specifying
explicit -L/--add-label labels. This works for the most part, but
doesn't cover the case where you don't know the parameterization of the
input data.
And we already have -s/-S flags in other csv scripts, so it makes sense
to adopt them in plot.py/plotmpl.py to allow sorting by one or more
explicit fields.
Note that -s/-S can be combined with explicit -L/--add-labels to order
datasets with the same sort field:
$ ./scripts/plot.py bench.csv \
-bBLOCK_SIZE \
-xn \
-ybench_readed \
-ybench_proged \
-ybench_erased \
--legend \
-sBLOCK_SIZE \
-L'*,bench_readed=bs=%(BLOCK_SIZE)s' \
-L'*,bench_proged=' \
-L'*,bench_erased='
---
Unfortunately this conflicted with -s/--sleep, which is a common flag in
the ascii-art scripts. This was bound to conflict with -s/--sort
eventually, so a came up with some alternatives:
- -s/--sleep -> -~/--sleep
- -S/--coalesce -> -+/--coalesce
But I'll admit I'm not the happiest about these...
This commit is contained in:
+1
-1
@@ -1711,7 +1711,7 @@ if __name__ == "__main__":
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help="Pipe directly to stdout.")
|
help="Pipe directly to stdout.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-s', '--sleep',
|
'-~', '--sleep',
|
||||||
type=float,
|
type=float,
|
||||||
help="Time in seconds to sleep between redraws when running "
|
help="Time in seconds to sleep between redraws when running "
|
||||||
"with -k. Defaults to 2 seconds.")
|
"with -k. Defaults to 2 seconds.")
|
||||||
|
|||||||
+1
-1
@@ -5161,7 +5161,7 @@ if __name__ == "__main__":
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help="Pipe directly to stdout.")
|
help="Pipe directly to stdout.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-s', '--sleep',
|
'-~', '--sleep',
|
||||||
type=float,
|
type=float,
|
||||||
help="Time in seconds to sleep between redraws when running "
|
help="Time in seconds to sleep between redraws when running "
|
||||||
"with -k. Defaults to 2 seconds.")
|
"with -k. Defaults to 2 seconds.")
|
||||||
|
|||||||
+2
-2
@@ -1845,11 +1845,11 @@ if __name__ == "__main__":
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help="Pipe directly to stdout.")
|
help="Pipe directly to stdout.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-S', '--coalesce',
|
'-+', '--coalesce',
|
||||||
type=lambda x: int(x, 0),
|
type=lambda x: int(x, 0),
|
||||||
help="Number of operations to coalesce together.")
|
help="Number of operations to coalesce together.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-s', '--sleep',
|
'-~', '--sleep',
|
||||||
type=float,
|
type=float,
|
||||||
help="Seconds to sleep between draws, coalescing operations "
|
help="Seconds to sleep between draws, coalescing operations "
|
||||||
"in between.")
|
"in between.")
|
||||||
|
|||||||
+39
-4
@@ -314,6 +314,19 @@ def dat(x, *args):
|
|||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
# a simple reverse-key class
|
||||||
|
class Rev(co.namedtuple('Rev', 'a')):
|
||||||
|
__slots__ = ()
|
||||||
|
# yes we need all of these because we're a namedtuple
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.a > other.a
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self.a < other.a
|
||||||
|
def __le__(self, other):
|
||||||
|
return self.a >= other.a
|
||||||
|
def __ge__(self, other):
|
||||||
|
return self.a <= other.a
|
||||||
|
|
||||||
def collect(csv_paths, defines=[]):
|
def collect(csv_paths, defines=[]):
|
||||||
# collect results from CSV files
|
# collect results from CSV files
|
||||||
fields = []
|
fields = []
|
||||||
@@ -1185,6 +1198,7 @@ def main_(ring, csv_paths, *,
|
|||||||
x=None,
|
x=None,
|
||||||
y=None,
|
y=None,
|
||||||
define=[],
|
define=[],
|
||||||
|
sort=None,
|
||||||
labels=[],
|
labels=[],
|
||||||
chars=[],
|
chars=[],
|
||||||
line_chars=[],
|
line_chars=[],
|
||||||
@@ -1384,12 +1398,18 @@ def main_(ring, csv_paths, *,
|
|||||||
# note we don't need to filter by defines again
|
# note we don't need to filter by defines again
|
||||||
datasets_, dataattrs_ = fold(results, all_by, all_x, all_y)
|
datasets_, dataattrs_ = fold(results, all_by, all_x, all_y)
|
||||||
|
|
||||||
# order by labels
|
# sort datasets
|
||||||
datasets_ = co.OrderedDict(sorted(
|
datasets_ = co.OrderedDict(sorted(
|
||||||
datasets_.items(),
|
datasets_.items(),
|
||||||
key=lambda kv: labels_.key(kv[0])))
|
key=lambda kv: (
|
||||||
|
# sort by explicit sort fields
|
||||||
|
tuple((Rev if reverse else lambda x: x)(
|
||||||
|
dat(dataattrs_[kv[0]].get(k,''), 0))
|
||||||
|
for k, reverse in (sort or [])),
|
||||||
|
# order by labels
|
||||||
|
labels_.key(kv[0]))))
|
||||||
|
|
||||||
# and merge dataattrs
|
# merge dataattrs
|
||||||
mergedattrs_ = {k: v
|
mergedattrs_ = {k: v
|
||||||
for dataattr in dataattrs_.values()
|
for dataattr in dataattrs_.values()
|
||||||
for k, v in dataattr.items()}
|
for k, v in dataattr.items()}
|
||||||
@@ -1918,6 +1938,21 @@ if __name__ == "__main__":
|
|||||||
action='append',
|
action='append',
|
||||||
help="Only include results where this field is this value. May "
|
help="Only include results where this field is this value. May "
|
||||||
"include comma-separated options and globs.")
|
"include comma-separated options and globs.")
|
||||||
|
class AppendSort(argparse.Action):
|
||||||
|
def __call__(self, parser, namespace, value, option):
|
||||||
|
if namespace.sort is None:
|
||||||
|
namespace.sort = []
|
||||||
|
namespace.sort.append((value, option in {'-S', '--reverse-sort'}))
|
||||||
|
parser.add_argument(
|
||||||
|
'-s', '--sort',
|
||||||
|
nargs='?',
|
||||||
|
action=AppendSort,
|
||||||
|
help="Sort by this field.")
|
||||||
|
parser.add_argument(
|
||||||
|
'-S', '--reverse-sort',
|
||||||
|
nargs='?',
|
||||||
|
action=AppendSort,
|
||||||
|
help="Sort by this field, but backwards.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-L', '--add-label',
|
'-L', '--add-label',
|
||||||
dest='labels',
|
dest='labels',
|
||||||
@@ -2126,7 +2161,7 @@ if __name__ == "__main__":
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help="Pipe directly to stdout.")
|
help="Pipe directly to stdout.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-s', '--sleep',
|
'-~', '--sleep',
|
||||||
type=float,
|
type=float,
|
||||||
help="Time in seconds to sleep between redraws when running "
|
help="Time in seconds to sleep between redraws when running "
|
||||||
"with -k. Defaults to 2 seconds.")
|
"with -k. Defaults to 2 seconds.")
|
||||||
|
|||||||
+38
-3
@@ -198,6 +198,19 @@ def dat(x, *args):
|
|||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
# a simple reverse-key class
|
||||||
|
class Rev(co.namedtuple('Rev', 'a')):
|
||||||
|
__slots__ = ()
|
||||||
|
# yes we need all of these because we're a namedtuple
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.a > other.a
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self.a < other.a
|
||||||
|
def __le__(self, other):
|
||||||
|
return self.a >= other.a
|
||||||
|
def __ge__(self, other):
|
||||||
|
return self.a <= other.a
|
||||||
|
|
||||||
def collect(csv_paths, defines=[]):
|
def collect(csv_paths, defines=[]):
|
||||||
# collect results from CSV files
|
# collect results from CSV files
|
||||||
fields = []
|
fields = []
|
||||||
@@ -779,6 +792,7 @@ def main(csv_paths, output, *,
|
|||||||
x=None,
|
x=None,
|
||||||
y=None,
|
y=None,
|
||||||
define=[],
|
define=[],
|
||||||
|
sort=None,
|
||||||
labels=[],
|
labels=[],
|
||||||
colors=[],
|
colors=[],
|
||||||
formats=[],
|
formats=[],
|
||||||
@@ -949,12 +963,18 @@ def main(csv_paths, output, *,
|
|||||||
# note we don't need to filter by defines again
|
# note we don't need to filter by defines again
|
||||||
datasets_, dataattrs_ = fold(results, all_by, all_x, all_y)
|
datasets_, dataattrs_ = fold(results, all_by, all_x, all_y)
|
||||||
|
|
||||||
# order by labels
|
# sort datasets
|
||||||
datasets_ = co.OrderedDict(sorted(
|
datasets_ = co.OrderedDict(sorted(
|
||||||
datasets_.items(),
|
datasets_.items(),
|
||||||
key=lambda kv: labels_.key(kv[0])))
|
key=lambda kv: (
|
||||||
|
# sort by explicit sort fields
|
||||||
|
tuple((Rev if reverse else lambda x: x)(
|
||||||
|
dat(dataattrs_[kv[0]].get(k,''), 0))
|
||||||
|
for k, reverse in (sort or [])),
|
||||||
|
# order by labels
|
||||||
|
labels_.key(kv[0]))))
|
||||||
|
|
||||||
# and merge dataattrs
|
# merge dataattrs
|
||||||
mergedattrs_ = {k: v
|
mergedattrs_ = {k: v
|
||||||
for dataattr in dataattrs_.values()
|
for dataattr in dataattrs_.values()
|
||||||
for k, v in dataattr.items()}
|
for k, v in dataattr.items()}
|
||||||
@@ -1372,6 +1392,21 @@ if __name__ == "__main__":
|
|||||||
action='append',
|
action='append',
|
||||||
help="Only include results where this field is this value. May "
|
help="Only include results where this field is this value. May "
|
||||||
"include comma-separated options and globs.")
|
"include comma-separated options and globs.")
|
||||||
|
class AppendSort(argparse.Action):
|
||||||
|
def __call__(self, parser, namespace, value, option):
|
||||||
|
if namespace.sort is None:
|
||||||
|
namespace.sort = []
|
||||||
|
namespace.sort.append((value, option in {'-S', '--reverse-sort'}))
|
||||||
|
parser.add_argument(
|
||||||
|
'-s', '--sort',
|
||||||
|
nargs='?',
|
||||||
|
action=AppendSort,
|
||||||
|
help="Sort by this field.")
|
||||||
|
parser.add_argument(
|
||||||
|
'-S', '--reverse-sort',
|
||||||
|
nargs='?',
|
||||||
|
action=AppendSort,
|
||||||
|
help="Sort by this field, but backwards.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-L', '--add-label',
|
'-L', '--add-label',
|
||||||
dest='labels',
|
dest='labels',
|
||||||
|
|||||||
+2
-2
@@ -236,11 +236,11 @@ if __name__ == "__main__":
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help="Pipe directly to stdout.")
|
help="Pipe directly to stdout.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-S', '--coalesce',
|
'-+', '--coalesce',
|
||||||
type=lambda x: int(x, 0),
|
type=lambda x: int(x, 0),
|
||||||
help="Number of lines to coalesce together.")
|
help="Number of lines to coalesce together.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-s', '--sleep',
|
'-~', '--sleep',
|
||||||
type=float,
|
type=float,
|
||||||
help="Seconds to sleep between draws, coalescing lines in "
|
help="Seconds to sleep between draws, coalescing lines in "
|
||||||
"between.")
|
"between.")
|
||||||
|
|||||||
+1
-1
@@ -1515,7 +1515,7 @@ if __name__ == "__main__":
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help="Pipe directly to stdout.")
|
help="Pipe directly to stdout.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-s', '--sleep',
|
'-~', '--sleep',
|
||||||
type=float,
|
type=float,
|
||||||
help="Time in seconds to sleep between redraws when running "
|
help="Time in seconds to sleep between redraws when running "
|
||||||
"with -k. Defaults to 2 seconds.")
|
"with -k. Defaults to 2 seconds.")
|
||||||
|
|||||||
+1
-1
@@ -329,7 +329,7 @@ if __name__ == "__main__":
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help="Pipe directly to stdout.")
|
help="Pipe directly to stdout.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-s', '--sleep',
|
'-~', '--sleep',
|
||||||
type=float,
|
type=float,
|
||||||
help="Seconds to sleep between runs. Defaults to 2 seconds.")
|
help="Seconds to sleep between runs. Defaults to 2 seconds.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
Reference in New Issue
Block a user