aboutsummaryrefslogtreecommitdiff
path: root/trappy/wa/results.py
blob: 0beb9150e1ce5f7ae4ce256387c11e93aa18d593 (plain)
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#    Copyright 2015-2016 ARM Limited
#
# 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.
#

"""Parse the results from a Workload Automation run and show it in a
"pretty" table

"""

import os
import collections, csv, re
import pandas as pd
from matplotlib import pyplot as plt

class Result(pd.DataFrame):
    """A DataFrame-like class for storing benchmark results"""
    def __init__(self, *args, **kwargs):
        super(Result, self).__init__(*args, **kwargs)
        self.ax = None

    def init_fig(self):
        _, self.ax = plt.subplots()

    def enlarge_axis(self, data):
        """Make sure that the axis don't clobber some of the data"""

        (_, _, plot_y_min, plot_y_max) = plt.axis()

        concat_data = pd.concat(data[s] for s in data)
        data_min = min(concat_data)
        data_max = max(concat_data)

        # A good margin can be 10% of the data range
        margin = (data_max - data_min) / 10
        if margin < 1:
            margin = 1

        update_axis = False

        if data_min <= plot_y_min:
            plot_y_min = data_min - margin
            update_axis = True

        if data_max >= plot_y_max:
            plot_y_max = data_max + margin
            update_axis = True

        if update_axis:
            self.ax.set_ylim(plot_y_min, plot_y_max)

    def plot_results_benchmark(self, benchmark, title=None):
        """Plot the results of the execution of a given benchmark

        A title is added to the plot if title is not supplied
        """

        if title is None:
            title = benchmark.replace('_', ' ')
            title = title.title()

        self[benchmark].plot(ax=self.ax, kind="bar", title=title)
        plt.legend(bbox_to_anchor=(1.05, .5), loc=6)

    def plot_results(self):
        for bench in self.columns.levels[0]:
            self.plot_results_benchmark(bench)

def get_run_number(metric):
    found = False
    run_number = None

    if re.match("Overall_Score|score|FPS", metric):
        found = True

        match = re.search(r"(.+)[ _](\d+)", metric)
        if match:
            run_number = int(match.group(2))
            if match.group(1) == "Overall_Score":
                run_number -= 1
        else:
            run_number = 0

    return (found, run_number)

def get_results(path=".", name=None):
    """Return a pd.DataFrame with the results

    The DataFrame's rows are the scores.  The first column is the
    benchmark name and the second the id within it.  For benchmarks
    that have a score result, that's what's used.  For benchmarks with
    FPS_* result, that's the score.  E.g. glbenchmarks "score" is it's
    fps.

    An optional name argument can be passed.  If supplied, it overrides
    the name in the results file.

    """

    bench_dict = collections.OrderedDict()

    if os.path.isdir(path):
        path = os.path.join(path, "results.csv")

    with open(path) as fin:
        results = csv.reader(fin)

        for row in results:
            (is_result, run_number) = get_run_number(row[3])

            if is_result:
                if name:
                    run_id = name
                else:
                    run_id = re.sub(r"_\d+", r"", row[0])

                bench = row[1]
                try:
                    result = int(row[4])
                except ValueError:
                    result = float(row[4])

                if bench in bench_dict:
                    if run_id in bench_dict[bench]:
                        if run_number not in bench_dict[bench][run_id]:
                            bench_dict[bench][run_id][run_number] = result
                    else:
                        bench_dict[bench][run_id] = {run_number: result}
                else:
                    bench_dict[bench] = {run_id: {run_number: result}}

    bench_dfrs = {}
    for bench, run_id_dict in bench_dict.iteritems():
        bench_dfrs[bench] = pd.DataFrame(run_id_dict)

    return Result(pd.concat(bench_dfrs.values(), axis=1,
                            keys=bench_dfrs.keys()))

def combine_results(data):
    """Combine two DataFrame results into one

    The data should be an array of results like the ones returned by
    get_results() or have the same structure.  The returned DataFrame
    has two column indexes.  The first one is the benchmark and the
    second one is the key for the result.

    """

    res_dict = {}
    for benchmark in data[0].columns.levels[0]:
        concat_objs = [d[benchmark] for d in data]
        res_dict[benchmark] = pd.concat(concat_objs, axis=1)

    combined = pd.concat(res_dict.values(), axis=1, keys=res_dict.keys())

    return Result(combined)