Test Failed
Pull Request — master (#878)
by
unknown
03:49
created

StatsUtils.generate_figures()   D

Complexity

Conditions 11

Size

Total Lines 78
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 70
nop 3
dl 0
loc 78
rs 4.7618
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like savu.plugins.stats.stats_utils.StatsUtils.generate_figures() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import matplotlib.pyplot as plt
2
import pandas as pd
3
import h5py as h5
4
import numpy as np
5
6
class StatsUtils(object):
7
8
    _pattern_dict = {"projection": ["SINOGRAM", "PROJECTION", "TANGENTOGRAM", "4D_SCAN", "SINOMOVIE"],
9
                     "reconstruction": ["VOLUME_YZ", "VOLUME_XZ", "VOLUME_XY", "VOLUME_3D"]}
10
    _stats_list = ["max", "min", "mean", "mean_std_dev", "median_std_dev", "RMSD"]
11
12
    def generate_figures(self, filepath, savepath):
13
        f = h5.File(filepath, 'r')
14
        stats_dict, index_dict = self._get_dicts_for_graphs(f)
15
        f.close()
16
        p_stats = pd.DataFrame(stats_dict["projection"], index_dict["projection"]["max"])
17
        r_stats = pd.DataFrame(stats_dict["reconstruction"], index_dict["reconstruction"]["max"])
18
        all_stats = pd.concat([p_stats, r_stats], keys=["Projection", "Reconstruction"])
19
        all_stats.to_html(f"{savepath}/stats_table.html")  # create table of all stats for all plugins
20
21
        stats_dict, array_plugins = self._remove_arrays(stats_dict, index_dict)
22
23
        p_stats_new = pd.DataFrame(stats_dict["projection"], index_dict["projection"]["max"])
24
        r_stats_new = pd.DataFrame(stats_dict["reconstruction"], index_dict["reconstruction"]["max"])
25
26
        colours = ["red", "blue", "green", "black", "purple", "brown"]
27
        # creating projection stats figure
28
        new_p_index = []
29
        p_legend = ""
30
        for ind in p_stats_new.index:
31
            new_p_index.append(ind[0])  # change x ticks to only be plugin numbers rather than names (for space)
32
            p_legend += f"{ind}\n"
33
        p_stats_new.index = new_p_index
34
        fig, ax = plt.subplots(3, 2, figsize=(11, 9), dpi=320, facecolor="azure")
35
        i = 0
36
        for row in ax:
37
            for axis in row:
38
                stat = self._stats_list[i]
39
                axis.plot(p_stats_new[stat], "x-", color=colours[i])
40
                for plugin in array_plugins["projection"]:  # adding 'error' bars for plugins with different values due to parameter changes
41
                    my_max = max(p_stats[stat][plugin])
42
                    my_min = min(p_stats[stat][plugin])
43
                    middle = (my_max + my_min) / 2
44
                    my_range = my_max - my_min
45
                    axis.errorbar(int(plugin[0]) - len(p_stats_new) + 1, middle, yerr=[my_range], capsize=5)
46
                if i == 1:
47
                    maxx = len(p_stats_new[stat]) * 1.08 - 1
48
                    maxy = max(p_stats_new[stat])
49
                    axis.text(maxx, maxy, p_legend, ha="left", va="top",
50
                              bbox=dict(boxstyle="round", facecolor="red", alpha=0.4))
51
                stat.replace("_", " ")
52
                axis.set_title(stat)
53
                axis.grid(True)
54
                i += 1
55
        fig.suptitle("Projection Stats", fontsize="x-large")
56
        plt.savefig(f"{savepath}/projection_stats.png", bbox_inches="tight")
57
58
        # creating reconstruction stats figure
59
        new_r_index = []
60
        r_legend = ""
61
        for ind in r_stats_new.index:
62
            new_r_index.append(ind[0])  # change x ticks to only be plugin numbers rather than names (for space)
63
            r_legend += f"{ind}\n"
64
        r_stats_new.index = new_r_index
65
66
        fig, ax = plt.subplots(3, 2, figsize=(11, 9), dpi=320, facecolor="lavender")
67
        i = 0
68
        for row in ax:
69
            for axis in row:
70
                stat = self._stats_list[i]
71
                axis.plot(r_stats_new[stat], "x-", color=colours[i])
72
                for plugin in array_plugins["reconstruction"]:  # adding 'error' bars for plugins with different values due to parameter changes
73
                    my_max = max(r_stats[stat][plugin])
74
                    my_min = min(r_stats[stat][plugin])
75
                    middle = (my_max + my_min) / 2
76
                    my_range = my_max - my_min
77
                    axis.errorbar(int(plugin[0]) - len(r_stats_new) + 1, middle, yerr=[my_range], capsize=5)
78
                if i == 1:
79
                    maxx = len(r_stats_new[stat]) * 1.08 - 1
80
                    maxy = max(r_stats_new[stat])
81
                    axis.text(maxx, maxy, r_legend, ha="left", va="top",
82
                              bbox=dict(boxstyle="round", facecolor="red", alpha=0.4))
83
                stat = stat.replace("_", " ")
84
                axis.set_title(stat)
85
                axis.grid(True)
86
                i += 1
87
88
        fig.suptitle("Reconstruction Stats", fontsize="x-large")
89
        plt.savefig(f"{savepath}/reconstruction_stats.png", bbox_inches="tight")
90
91
    @staticmethod
92
    def _get_dicts_for_graphs(file):
93
        stats_dict = {}
94
        index_dict = {}
95
        stats_dict["projection"] = {"max": [], "min": [], "mean": [], "mean_std_dev": [], "median_std_dev": [],
96
                                    "RMSD": []}
97
        stats_dict["reconstruction"] = {"max": [], "min": [], "mean": [], "mean_std_dev": [], "median_std_dev": [],
98
                                        "RMSD": []}
99
        index_dict["projection"] = {"max": [], "min": [], "mean": [], "mean_std_dev": [], "median_std_dev": [],
100
                                    "RMSD": []}
101
        index_dict["reconstruction"] = {"max": [], "min": [], "mean": [], "mean_std_dev": [], "median_std_dev": [],
102
                                        "RMSD": []}
103
104
        group = file["stats"]
105
        for space in ("projection", "reconstruction"):
106
            for index, stat in enumerate(["max", "min", "mean", "mean_std_dev", "median_std_dev", "RMSD"]):
107
                for key in list(group.keys()):
108
                    if group[key].attrs.get("pattern") in StatsUtils._pattern_dict[space]:
109
                        index_dict[space][stat].append(f"{key}: {group[key].attrs.get('plugin_name')}")
110
                        if group[key].ndim == 1:
111
                            if len(group[key]) > index:
112
                                stats_dict[space][stat].append(group[key][index])
113
                            else:
114
                                stats_dict[space][stat].append(None)
115
                        elif group[key].ndim == 2:
116
                            if len(group[key][0]) > index:
117
                                stats_dict[space][stat].append(group[key][:, index])
118
                            else:
119
                                stats_dict[space][stat].append(None)
120
        return stats_dict, index_dict
121
122
123
    @staticmethod
124
    def _remove_arrays(stats_dict, index_dict):
125
        array_plugins = {"projection": set(()), "reconstruction": set(())}
126
        for space in list(stats_dict.keys()):
127
            for stat in list(stats_dict[space].keys()):
128
                for index, value in enumerate(stats_dict[space][stat]):
129
                    if isinstance(value, np.ndarray):
130
                        stats_dict[space][stat][index] = stats_dict[space][stat][index][0]
131
                        array_plugins[space].add(index_dict[space][stat][index])
132
        return stats_dict, array_plugins
133
134
135