Skip to content

Commit

Permalink
r.boxplot added functionality (#749)
Browse files Browse the repository at this point in the history
Added options: color the boxplots according to the color of the corresponding category of the zonal raster, and set font size.
  • Loading branch information
ecodiv committed May 8, 2022
1 parent 787e300 commit 00caf54
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 10 deletions.
32 changes: 25 additions & 7 deletions src/raster/r.boxplot/r.boxplot.html
Expand Up @@ -27,9 +27,11 @@ <h2>DESCRIPTION</h2>

<p>
There are a few layout options, including the option to rotate
the plot and the x-axis labels, print the boxplot(s) with notches and
sort the boxplot from low to high (ascending) or from high to low (descending)
median.
the plot and the x-axis labels, print the boxplot(s) with notches, sort the
boxplot from low to high (ascending) or from high to low (descending) median,
and color the boxplots according to the corresponding categories of the zonal
raster.


<h2>NOTE</h2>
To include outliers, the function converts the raster cell with outlier
Expand All @@ -50,7 +52,7 @@ <h3>Example 1</h3>
g.region raster=elevation
r.boxplot -h input=elevation plot_dimensions="7,1" output="r_boxplot_01.png"
</pre>
</div>
</div><br>

<p>
<img src="r_boxplot_01.png"><br>
Expand All @@ -64,7 +66,7 @@ <h3>Example 2</h3>
<div class="code"><pre>
r.boxplot -r input=elevation zone=landclass96 output="r_boxplot_02.png"
</pre>
</div>
</div><br>

<p>
<img src="r_boxplot_02.png"><br>
Expand All @@ -80,11 +82,10 @@ <h3>Example 3</h3>
<div class="code"><pre>
r.boxplot -o order=ascending input=elevation zones=landclass96 output="r_boxplot_03.png" map_outliers="outliers"
</pre>
</div>
</div><br>

<p>
<img src="r_boxplot_03.png"><br>
<em>Figure 3: Boxplot of elevation raster layer per land use zone, including outliers.</em>

<p>
Below, part of the landclass96 raster map is shown, with on top the vector point
Expand All @@ -94,6 +95,22 @@ <h3>Example 3</h3>
<p>
<img src="r_boxplot_map_03.png"><br>

<h3>Example 4</h3>
Draw boxplots of the values of the <em>elevation</em> layer per category from
the <i>landclass96</i> layer from the same
<a href="https://grass.osgeo.org/download/data/">NC sample
dataset</a>. Set the -c flag to color the boxplots, use order=ascending
to order the boxplots from low to high median, and set the fontsize to 11.

<div class="code"><pre>
r.boxplot -c order=ascending fontsize=11 input=elevation zones=landclass96 output="r_boxplot_04.png"
</pre>
</div><br>

<p>
<img src="r_boxplot_04.png"><br>


<h2>Acknowledgements</h2>
This work was carried in the framework of the <a href="https://savethetiger.nl/" target="_blank">Save the tiger, save the grassland, save the water</a>
project by the
Expand All @@ -112,3 +129,4 @@ <h2>AUTHOR</h2>
Paulo van Breugel<br>
Applied Geo-information Sciences<br>
<a href="https://www.hasuniversity.nl/">HAS University of Applied Sciences</a><br>

80 changes: 77 additions & 3 deletions src/raster/r.boxplot/r.boxplot.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python

############################################################################
#
# MODULE: r.boxplot
Expand Down Expand Up @@ -73,6 +74,15 @@
# % required: no
# %end

# %option
# % key: fontsize
# % type: integer
# % label: Font size
# % description: Default font size
# % guisection: Plot options
# % required: no
# %end

# %flag
# % key: h
# % label: horizontal boxplot(s)
Expand Down Expand Up @@ -119,10 +129,22 @@
# % guisection: Plot options
# %end

# %flag
# % key: c
# % label: Color boxploxs
# % description: Color boxploxs using the colors of the categories of the zonal raster
# % guisection: Plot options
# %end

# %rules
# % requires: map_outliers, -o
# %end

# %rules
# % requires: -c, zones
# %end


import sys
import atexit
import uuid
Expand Down Expand Up @@ -210,6 +232,7 @@ def bxp_nozones(
rotate_label,
name_outliers_map,
whisker_range,
fontsize,
):
"""Compute the statistics used to create the boxplot,
and create the boxplot. This function is used in case
Expand Down Expand Up @@ -314,14 +337,16 @@ def bxp_nozones(
else:
fliers = []

# Set plot dimensions
# Set plot dimensions and fontsize
if dimensions:
dimensions = [float(x) for x in dimensions.split(",")]
else:
if vertical:
dimensions = [4, 8]
else:
dimensions = [8, 4]
if fontsize:
plt.rcParams["font.size"] = fontsize

# Create plot
_, ax = plt.subplots(figsize=dimensions)
Expand Down Expand Up @@ -369,6 +394,8 @@ def bxp_zones(
sort,
name_outliers_map,
whisker_range,
bpcolors,
fontsize,
):
"""Compute the statistics used to create the boxplot,
and create the boxplots per zone from the zonal map."""
Expand All @@ -393,6 +420,27 @@ def bxp_zones(
labels = [_f for _f in labels if _f]
labels = [_y[1] for _y in [_x.split("|") for _x in labels]]

# Get colors
if bpcolors:
zones_color = Module("r.colors.out", map=zones, stdout_=PIPE).outputs.stdout
zones_color = zones_color.replace("\r", "").split("\n")
zones_color = [_f for _f in zones_color if _f]
zones_color = [
_x
for _x in zones_color
if not _x.startswith("nv") and not _x.startswith("default")
]
zones_color = [_y[1] for _y in [_x.split(" ") for _x in zones_color]]
zones_color = [_z.split(":") for _z in zones_color]
zones_rgb = [[int(_x) / 255 for _x in _y] for _y in zones_color]
txt_rgb = []
for i in zones_color:
rgb_i = list(map(int, i))
if rgb_i[0] * 0.299 + rgb_i[1] * 0.587 + rgb_i[2] * 0.114 > 149:
txt_rgb.append([0, 0, 0, 0.7])
else:
txt_rgb.append([1, 1, 1, 0.7])

# Compute statistics
quantstats = Module(
"r.stats.quantile",
Expand All @@ -418,6 +466,9 @@ def bxp_zones(
ordered_list = [i for _, i in sorted(zip(medians, ids), reverse=False)]
else:
ordered_list = list(range(0, len(order_bpl)))
if bpcolors:
zones_rgb[:] = [zones_rgb[i] for i in ordered_list]
txt_rgb[:] = [txt_rgb[i] for i in ordered_list]

# Define the boxes
boxes = []
Expand Down Expand Up @@ -551,18 +602,32 @@ def bxp_zones(
elif name_outliers_map:
gs.message("\n--> There are no outliers")

# Set plot dimensions
# Set plot dimensions and fontsize
if dimensions:
dimensions = [float(x) for x in dimensions.split(",")]
else:
if vertical:
dimensions = [8, 4]
else:
dimensions = [4, 8]
if fontsize:
plt.rcParams["font.size"] = fontsize

# Plot the figure
_, ax = plt.subplots(figsize=dimensions)
ax.bxp(boxes, showfliers=True, widths=0.6, vert=vertical, shownotches=notch)
bxplot = ax.bxp(
boxes,
showfliers=True,
widths=0.6,
vert=vertical,
shownotches=notch,
patch_artist=bpcolors,
)
if bpcolors:
for patch, color in zip(bxplot["boxes"], zones_rgb):
patch.set_facecolor(color)
for median, mcolor in zip(bxplot["medians"], txt_rgb):
median.set_color(mcolor)
if vertical:
ax.set_ylabel(strip_mapset(name))
else:
Expand All @@ -589,7 +654,13 @@ def main(options, flags):
if zones_raster:
check_integer(zones_raster)
output = options["output"]
fontsize = options["fontsize"]
if fontsize:
int(fontsize)
else:
fontsize = 10
whisker_range = float(options["range"])
bpcolor = flags["c"]
if whisker_range <= 0:
gs.fatal(_("The range value need to be larger than 0"))
if flags["h"]:
Expand Down Expand Up @@ -622,6 +693,8 @@ def main(options, flags):
sort=sort,
name_outliers_map=name_outliers_map,
whisker_range=whisker_range,
bpcolors=bpcolor,
fontsize=fontsize,
)
else:
bxp_nozones(
Expand All @@ -635,6 +708,7 @@ def main(options, flags):
rotate_label=rotate_label,
name_outliers_map=name_outliers_map,
whisker_range=whisker_range,
fontsize=fontsize,
)


Expand Down
Binary file added src/raster/r.boxplot/r_boxplot_04.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 00caf54

Please sign in to comment.