Skip to content

plot_prop_report

Command line utility for plotting property reports.

call_plot_traces(args, trace_values)

Call the internal plot_traces function and, optionally, save the results to disk.

Source code in emod_api/channelreports/plot_prop_report.py
 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
def call_plot_traces(args: argparse.Namespace,
                     trace_values: Dict[str, np.ndarray]) -> None:

    """
    Call the internal `plot_traces` function and, optionally, save the results to disk.
    """

    if args.verbose:
        print(sorted(trace_values))

    if args.normalize:
        stat_pop = "Statistical Population"
        traces = {key: value for (key, value) in trace_values.items() if not key.startswith(stat_pop)}
        # reduce the various statistical population traces to a single vector
        norms = reduce(lambda x, y: np.array(y) + x, [value for (key, value) in trace_values.items() if key.startswith(stat_pop)], 0)
    else:
        traces = trace_values
        norms = None

    figure = plot_traces(traces, norms, args.overlay, args.channels, args.filename, args.legend)

    if args.saveFigure:
        print("Saving figure 'propertyReport.png'...")
        figure.savefig('propertyReport.png')

    plt.show()

    return

list_channels_and_ips(channel_keys)

List the channels and properties found in a property report from the CHANNEL:IP:value,...,IP:value keys of the channel dictionary.

Source code in emod_api/channelreports/plot_prop_report.py
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
def list_channels_and_ips(channel_keys: List[str]) -> None:

    """
    List the channels and properties found in a property report from the
    CHANNEL:IP:value,...,IP:value keys of the channel dictionary.
    """

    # keys look like "CHANNEL:IP:value,...,IP:value"
    channels = sorted(set([key.split(":", 1)[0] for key in channel_keys]))

    print("\nChannels:")
    for channel in channels:
        print(f"\t{channel}")

    # Each channel _should_ have the same set of IPs, but we'll check them all
    csvkvps = [key.split(":", 1)[1] for key in channel_keys]            # For each channel get a comma separated list of IP:value pairs (see format above)
    kvplists = [csv.split(",") for csv in csvkvps]                      # For each CSV convert to actual list by splitting on ","
    ips = [map(lambda t: t.split(":")[0], kvps) for kvps in kvplists]   # Convert each IP:value entry to just IP
    properties = sorted(reduce(lambda s, e: s.union(e), ips, set()))    # Add all IPs to an initially empty set

    print("\nIPs:")
    for prop in properties:
        print(f"\t{prop}")

    print()

    return

main(args)

Plot specified property report with the given options.

Source code in emod_api/channelreports/plot_prop_report.py
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
def main(args: argparse.Namespace):

    """
    Plot specified property report with the given options.
    """

    json_data = read_json_file(args.filename)
    channel_data = get_report_channels(json_data)
    channel_keys = sorted(channel_data)

    if args.verbose:
        print("Channels:Pools-")
        print(json.dumps(channel_keys, indent=4))

    if args.list:
        list_channels_and_ips(channel_keys)
        return

    _validate_property_report_channels(args.channels, channel_data)
    _validate_property_report_ips(args.groupby, channel_data)

    if args.normalize and ("Statistical Population" not in args.channels):
        args.channels.append("Statistical Population")

    trace_values = accumulate_channel_data(args.channels, args.verbose, args.groupby, channel_data)

    if args.csv is None:
        call_plot_traces(args, trace_values)
    else:
        save_to_csv(trace_values, args.csv, args.transpose)

    return

process_cmd_line()

Put command line processing here rather than in if 'name' == '__main__'.

Source code in emod_api/channelreports/plot_prop_report.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def process_cmd_line() -> argparse.Namespace:

    """
    Put command line processing here rather than in `if 'name' == '__main__'`.
    """

    parser = argparse.ArgumentParser(description='Property Report Plotting')
    parser.add_argument('filename', nargs='?', default='PropertyReport.json', help='property report filename [PropertyReport.json]')
    parser.add_argument('-c', '--channel', action='append', help='channel(s) to display [Infected]', metavar='channelName', dest='channels')
    parser.add_argument('-g', '--groupby', action='append', help="IP(s) under which to aggregate other IP keys and values")
    parser.add_argument('-n', '--normalize', help='plot channel(s) normalized by statistical population', action='store_true')
    parser.add_argument('-o', '--overlay', help='overlay pools of the same channel', action='store_true')
    parser.add_argument('-s', '--save', help="save figure to file 'propertyReport.png'", action='store_true', dest='saveFigure')
    parser.add_argument('-v', '--verbose', action="store_true")
    parser.add_argument('--no-legend', action="store_false", dest="legend")     # Note args.legend default to True, passing --no-legend sets args.legend to False
    parser.add_argument('-l', '--list', action="store_true", help="List channels and IP keys found in the report. No plotting is performed with this option.")
    parser.add_argument("--csv", type=Path, default=None, help="Write data for selected channel(s) to given file.")
    parser.add_argument("-t", "--transpose", action="store_true", help="write channels as columns rather than rows (only in effect with '--csv' option)")

    args = parser.parse_args()

    if not args.channels:
        args.channels = ['Infected']

    if args.groupby is not None and len(args.groupby) == 1 and args.groupby[0].lower() == "all":
        args.groupby = []

    if not args.list:
        print(f"Filename:              '{args.filename}'")
        print(f"Channel(s):            {args.channels}")
        print(f"Groupby:               {args.groupby}")
        print(f"Normalize:             {args.normalize}")
        print(f"Overlay:               {args.overlay}")
        print(f"Save:                  {args.saveFigure}")
        if args.csv:
            print(f"CSV filename:          '{args.csv}'")
        print(f"Transpose CSV:         {args.transpose}")

    return args

prop_report_json_to_csv(output_path, channel_name='Infected', groupby='Geographic')

Converts selected channel of PropertyReportXXX.json into a CSV file, rolled up into a single property.

Parameters:

Name Type Description Default
output_path str

Subdirectory in which to find a file called PropertyReportXXX.json. XXX can be blank or a disease named like 'TB'.

required
channel_name str

Name of the channel to process from the property report. Defaults to "Infected".

'Infected'
groupby str

Property to group by. Defaults to "Geographic".

'Geographic'

Returns:

Raises:

Type Description
ValueError

If no PropertyReportXXX.json file is found in the directory.

Source code in emod_api/channelreports/plot_prop_report.py
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
def prop_report_json_to_csv(output_path: str,
                            channel_name: str = "Infected",
                            groupby: str = "Geographic"):
    """
    Converts selected channel of PropertyReportXXX.json into a CSV file, rolled up into a single property.

    Args:
        output_path: Subdirectory in which to find a file called PropertyReportXXX.json.
            XXX can be blank or a disease named like 'TB'.
        channel_name: Name of the channel to process from the property report.
            Defaults to "Infected".
        groupby: Property to group by. Defaults to "Geographic".

    Returns:

    Raises:
        ValueError: If no PropertyReportXXX.json file is found in the directory.
    """

    def find_file_starting_with(directory, prefix):
        path = Path(directory)
        for file in path.iterdir():
            if file.name.startswith(prefix) and file.is_file() and file.name.endswith("json"):
                return str(file)
        return None

    prop_report_path = find_file_starting_with(output_path, "PropertyReport")
    if not prop_report_path:
        raise ValueError(f"No json file starting with 'PropertyReport' found in '{output_path}'.")

    # This class probably exists somewhere else. Maybe we can move it to a common utils.py file or getit from elsewhere.
    class DynamicObject:
        def __init__(self):
            object.__setattr__(self, 'members', {})

        def __setattr__(self, name, value):
            self.members[name] = value

        def __getattr__(self, name):
            return self.members.get(name)

    faux_args = DynamicObject()
    faux_args.filename = prop_report_path
    csv_out_name = "prop_report_" + channel_name.replace(' ', '_').lower() + ".csv"
    faux_args.csv = csv_out_name
    faux_args.channels = [channel_name]
    faux_args.channels.append("Statistical Population")
    faux_args.normalize = True
    faux_args.groupby = [groupby]
    main(faux_args)