@@ -18,92 +18,128 @@ and `ca_policies` have already been solved).
1818
1919## Adding new graphs
2020
21- Graphs can be defined in any module by adding the following function to the file.
22-
21+ New graphs can be added with the ` @graph(...) ` annotation.
2322``` python
24- def graph (tools ):
23+ from switch_model.tools.graph import graph
24+
25+ @graph (
26+ name = " my_custom_graph" ,
27+ title = " An example plot" ,
28+ note = " Some optional note to add below the graph" ,
29+ # Other options are possible see code documentation
30+ )
31+ def my_graphing_function (tools ):
2532 # Your graphing code
2633 ...
2734```
2835
29- In ` graph() ` you can use the ` tools ` object to create graphs. Here are some important methods.
30-
31- - ` tools.get_dataframe(csv=filename) ` will return a pandas dataframe for the file called ` filename ` . You can also
32- specify ` folder=tools.folders.INPUTS ` to load a csv from the inputs directory.
33-
34- - ` tools.get_new_axes(out, title, note) ` will return a matplotlib axes. This should be the axes used while
35- graphing. ` out ` is the name of the ` .png ` file that will be created with this graph. ` title ` and ` note ` are optional
36- and will be the title and footnote for the graph.
37-
38- - ` tools.pd ` , ` tools.sns ` , ` tools.np ` , ` tools.mplt ` are references to the pandas, seaborn, numpy and matplotlib
39- libraries. This is useful if your graphing code needs to access these libraries since it doesn't require adding an
36+ In ` my_graphing_function() ` you can use the ` tools ` object to create graphs. Here are some important methods.
37+
38+ - ` tools.get_dataframe(filename) ` will return a pandas dataframe for the file called ` filename ` . You can also
39+ specify ` from_inputs=True ` to load a csv from the inputs directory.
40+
41+ - ` tools.get_axes() ` or ` tools.get_figure() ` will return a matplotlib axes or figure
42+ that should be used while graphing. When possible, always use ` get_axes ` instead of ` get_figure ` since
43+ this allows plots from different scenarios to share the same figure.
44+
45+ - ` tools.save_figure(fig) ` . Some libraries (e.g. plotnine)
46+ always generate their own figures. In this case we can add the figure
47+ to our outputs with this function. When possible, use ` tools.get_axes() ` instead.
48+
49+ - ` tools.pd ` , ` tools.sns ` , ` tools.np ` , ` tools.mplt ` , ` tools.pn ` are references to the pandas, seaborn, numpy, matplotlib
50+ and plotnine graphing libraries. This is useful if your graphing code needs to access these libraries since it doesn't require adding an
4051 import to your file.
4152
42- - ` tools.add_gen_type_column(df) ` adds a column called ` gen_type ` to a dataframe with columns
43- ` gen_tech ` and ` gen_energy_source ` . ` gen_type ` is a user-friendly name for the technology (e.g. Nuclear instead of
44- Uranium). The mapping of ` gen_energy_source ` and ` gen_tech ` to ` gen_type ` is defined in
45- a ` inputs/graph_tech_types.csv ` . If this file isn't present, a default mapping will be used. You can also use other
46- mappings found in ` graph_tech_types.csv ` by specifying ` map_name= ` when calling ` add_gen_type_column() ` .
47-
53+ - ` tools.transform ` is a reference to a ` TransformTools ` object that provides
54+ useful helper methods for modyfing a dataframe for graphing. Full documentation
55+ can be found in the ` TransformTools ` class but some examples include.
56+
57+ - ` tools.transform.build_year(df) ` which will convert build years that aren't
58+ a period to the string ` Pre-existing ` .
59+
60+ - ` tools.transform.gen_type(df) ` which adds a column called ` gen_type ` to the dataframe.
61+ ` gen_type ` is a user-friendly name for the technology (e.g. Nuclear instead of
62+ Uranium) and is determined using the mappings in ` inputs/graph_tech_types.csv ` .
63+
64+ - ` tools.transform.timestamp(df) ` : which adds columns such as the hour, the timestamp in datetime format
65+ in the correct timezone, etc.
66+
67+ - ` tools.transform.load_zone(df) ` : Adds a column called 'region' to the dataframe which
68+ normally corresponds to the load zone state.
69+
4870- ` tools.get_colors() ` returns a mapping of ` gen_type ` to its color. This is useful for graphing and can normally be
49- passed straight to ` color= ` in standard plotting libraries. You can also specify a different color mapping using a
50- similar process to above (` map_name= ` )
71+ passed straight to ` color= ` in standard plotting libraries. The color mapping is based on ` inputs/graph_tech_colors.csv ` .
5172
5273## Adding a comparison graph
5374
54- By default, ` tools.get_dataframe ` will return the data for only one scenario (the one you are graphing).
55-
56- Sometimes, you may wish to create a graph that compares multiple scenarios. To do this create a function
57- called ` compare ` .
75+ Sometimes you may want to create graphs that compare data from multiple scenarios.
76+ To do this, add ` supports_multi_scenario=True ` inside the ` @graph() ` decorator.
5877
5978``` python
60- def compare (tools ):
61- # Your graphing code
62- ...
79+ from switch_model.tools.graph import graph
80+
81+ @graph (
82+ name = " my_custom_comparison_graph" ,
83+ title = " My Comparison plot" ,
84+ supports_multi_scenario = True ,
85+ # Instead of supports_multi_scenario, you can use
86+ # requires_multi_scenario if you want the graphing function
87+ # to *only* be run when we have multiple scenarios.
88+ # requires_multi_scenario=True,
89+ )
90+ def my_graphing_comparison_function (tools ):
91+ # Read data from all the scenarios
92+ df = tools.get_dataframe(" some_file.csv" )
93+ # Plot data
94+ ...
6395```
6496
65- If you call ` tools.get_dataframe(...) ` from within ` compare ` , then
66- ` tools.get_dataframe ` will return a dataframe containing the data from * all*
67- the scenarios. The dataframe will contain a column called ` scenario ` to indicate which rows correspond to which
68- scenarios. You can then use this column to create a graph comparing the different scenarios (still
69- using ` tools.get_new_axes ` ).
97+ Now everytime you call ` tools.get_dataframe(filename) ` , data for * all* the scenarios
98+ gets returned. The way this works is that the
99+ returned dataframe will contain a column called ` scenario_name `
100+ to indicate which rows correspond to which scenarios.
101+ You can then use this column to create a graph comparing the different scenarios (still
102+ using ` tools.get_axes ` ).
70103
71- At this point, when you run ` switch compare ` , your ` compare(tools) ` function will be called and your comparison graph
104+ At this point, when you run ` switch compare ` , your ` my_graphing_comparison_function ` function will be called and your comparison graph
72105will be generated.
73106
74107## Example
75108
76109In this example we create a graph that shows the power capacity during each period broken down by technology.
77110
78111``` python
112+ from switch_model.tools.graph import graph
113+
114+ @graph (
115+ " capacity_per_period" ,
116+ title = " Capacity per period"
117+ )
79118def graph (tools ):
80- # Get a dataframe of gen_cap.csv
81- gen_cap = tools.get_dataframe(csv = " gen_cap" )
82-
83- # Add a 'gen_type' column to your dataframe
84- gen_cap = tools.add_gen_type_column(gen_cap)
85-
86- # Aggregate the generation capacity by gen_type and PERIOD
87- capacity_df = gen_cap.pivot_table(
88- index = ' PERIOD' ,
89- columns = ' gen_type' ,
90- values = ' GenCapacity' ,
91- aggfunc = tools.np.sum,
92- fill_value = 0 # Missing values become 0
93- )
94-
95- # Get a new pair of axis to plot onto
96- ax = tools.get_new_axes(out = " capacity_per_period" )
97-
98- # Plot
99- capacity_df.plot(
100- kind = ' bar' ,
101- ax = ax, # Notice we pass in the axis
102- stacked = True ,
103- ylabel = " Capacity Online (MW)" ,
104- xlabel = " Period" ,
105- color = tools.get_colors(len (capacity_df.index))
106- )
119+ # Get a dataframe of gen_cap.csv
120+ df = tools.get_dataframe(" gen_cap.csv" )
121+
122+ # Add a 'gen_type' column to your dataframe
123+ df = tools.transform.gen_type(df)
124+
125+ # Aggregate the generation capacity by gen_type and PERIOD
126+ df = df.pivot_table(
127+ index = ' PERIOD' ,
128+ columns = ' gen_type' ,
129+ values = ' GenCapacity' ,
130+ aggfunc = tools.np.sum,
131+ fill_value = 0 # Missing values become 0
132+ )
133+
134+ # Plot
135+ df.plot(
136+ kind = ' bar' ,
137+ ax = tools.get_axes(),
138+ stacked = True ,
139+ ylabel = " Capacity Online (MW)" ,
140+ xlabel = " Period" ,
141+ color = tools.get_colors(len (df.index))
142+ )
107143```
108144
109145Running ` switch graph ` would run the ` graph() ` function above and create
@@ -112,3 +148,37 @@ Running `switch graph` would run the `graph()` function above and create
112148Running ` switch compare ` would create ` capacity_per_period.png ` containing
113149your plot side-by-side with the same plot but for the scenario you're comparing to.
114150
151+ ### Testing your graphs
152+
153+ To test your graphs, you can run ` switch graph ` or ` switch compare ` . However,
154+ this takes quite some time. If you want to test just one graphing function
155+ you can run ` switch graph/compare -f FIGURE ` . This will run only the graphing function
156+ you've defined. Here ` FIGURE ` should be the name of the graph (the first
157+ argument in ` @graph() ` , so ` capacity_per_period ` in the example above).
158+
159+ ### Creating graphs outside of SWITCH
160+
161+ Sometimes you may want to create graphs but don't want to permently add
162+ them to the switch code. To do this create the following Python file anywhere
163+ on your computer.
164+
165+ ``` python
166+ from switch_model.tools.graph import graph
167+ from switch_model.tools.graph.cli_graph import main as graph
168+
169+ @graph (
170+ ...
171+ )
172+ def my_first_graph (tools ):
173+ ...
174+
175+ @graph (
176+ ...
177+ )
178+ def my_second_graph (tools ):
179+ ...
180+
181+ if __name__ == " __main__" :
182+ graph([" --ignore-modules-txt" ])
183+ ```
184+
0 commit comments