1

I'd like to produce a plot like the following using openpyxl:

enter image description here

To create it I followed the approach of combining a line chart with a bar chart where the bars are formatted to be the background vertical bands.

I came very close to achieving it but I was unable to:

  • hide the tick marks of the secondary y-axis
  • hide the major grid lines of the secondary y-axis
  • make the colour of the vertical bands transparent so the major grid lines of the primary y-axis are visible

I was able get the following result with the issues listed above:

sad plot

Below is the code the produces the chart above (using openpyxl 2.3.5 and Python 2.7.11):

from openpyxl import Workbook
from openpyxl.chart import BarChart
from openpyxl.chart import LineChart
from openpyxl.chart import Reference
from openpyxl.chart.data_source import NumFmt
from openpyxl.chart.marker import DataPoint
from openpyxl.chart.series_factory import SeriesFactory
from openpyxl.chart.axis import TextAxis
from openpyxl.chart.shapes import GraphicalProperties


workbook = Workbook()
worksheet = workbook.active

rows = [
    ['region', 'value', 'index', 'unity'],
    ['A1', 0.9, 1, 1],
    ['A2', 1.1, 2, 1],
    ['B1', 1.6, 3, 1],
    ['C1', 0.9, 4, 1],
    ['D1', 1.0, 5, 1],
    ['D2', 1.0, 6, 1],
    ['E1', 0.9, 7, 1],
]

for row in rows:
    worksheet.append(row)

verticalbands = BarChart()
verticalbands.type = "col"
verticalbands.grouping = "stacked"
verticalbands.overlap = 100
verticalbands.gapWidth = 0

colours = ["c4bedf", "c4bedf", "d6d8b4", "98d4e4", "e6b8b3", "e6b8b3", "aad2bf"]
xvalues = Reference(worksheet, min_col=3, min_row=2, max_row=8)
data = Reference(worksheet, min_col=4, min_row=2, max_row=8)
series = SeriesFactory(data)
verticalbands.x_axis = TextAxis()
verticalbands.set_categories(xvalues)
verticalbands.series.append(series)

for i, colour in zip(range(9), colours):
    pt = DataPoint(idx=i)
    pt.graphicalProperties = GraphicalProperties(solidFill=colour)
    series.dPt.append(pt)

verticalbands.y_axis.numFmt = NumFmt(formatCode=";")  #  hide value, see https://www.ablebits.com/office-addins-blog/2016/07/07/custom-excel-number-format/
verticalbands.y_axis.scaling.min = 0
verticalbands.y_axis.scaling.max = 1
verticalbands.y_axis.majorTickMark = None
verticalbands.y_axis.minorTickMark = None

data_chart = LineChart()
xvalues = Reference(worksheet, min_col=1, min_row=2, max_row=8)
yvalues = Reference(worksheet, min_col=2, min_row=1, max_row=8)
series = SeriesFactory(yvalues, title_from_data=True)
series.marker.symbol = 'circle'
series.marker.size = 4
series.marker.graphicalProperties.solidFill = "4F81BD"
series.marker.graphicalProperties.line.noFill = True
series.graphicalProperties.line.noFill = True
data_chart.series.append(series)
data_chart.x_axis = TextAxis()
data_chart.set_categories(xvalues)

verticalbands.y_axis.crosses = 'max'
verticalbands.y_axis.axId = 200

verticalbands += data_chart

worksheet.add_chart(verticalbands, "A12")

workbook.save('~/file.xlsx)

It is frustrating that these lines do not have the desired effect:

verticalbands.y_axis.majorTickMark = None
verticalbands.y_axis.minorTickMark = None

To see the problem, I have to manually set the transparency of the rightmost band to 50%:

enter image description here

It would be great to simply "delete" the axis:

verticalbands.y_axis.delete = True

but it just breaks the chart:

enter image description here

For the transparency, I tried using 8-character RGB hex colours but the solidFill parameter of GraphicalProperties cannot take colours like 00FF0000 resulting in an invalid file that Excel cannot open.

Any idea on how to overcome these issues?

Pablo
  • 983
  • 10
  • 24
  • What happens if you work with a file with such a chart already in it? – Charlie Clark Oct 18 '18 at 13:51
  • I have not tried that; however there is the risk of openpyxl being unable to open a file saved in Excel by the user because of the many Excel versions. – Pablo Oct 18 '18 at 14:02
  • OOXML is spec'd for 2010 and later so it's worth giving it a try. The way charts are combined is a bit different but if it works then it will save you some work. Otherwise you're going have to look carefully at the XML, especially the awful DrawingML that we only partially support. Feel free to discuss this on the mailing list and open issues on the bug tracker where stuff isn't supported. – Charlie Clark Oct 18 '18 at 14:06
  • Thank you for the pointer! openpyxl is amazing tool, you've done a great job :) – Pablo Oct 19 '18 at 16:09
  • Is there a way to fill each marker separately with different colors? – Jarrett Jan 25 '22 at 07:34

0 Answers0