3
$\begingroup$

I am struggling to obtain the numerical values {x,y} of the barcharts in this plot. Ideally with an automated routine.

enter image description here

Time ago, I developed a routine to do it in the case of points on the plot (eg the typical outcome of a ListPlot command). But it does not work with barcharts. Furthermore, another challenge is that ,here, you do not really see the tickmarks on the x- and y- axis.

$\endgroup$
1
  • $\begingroup$ One non-mma solution is to use WebPlotDigitizer , although it makes it more difficult that there are no axes nor any ticks $\endgroup$
    – ydd
    Sep 5, 2023 at 15:17

1 Answer 1

5
$\begingroup$

This is almost completely automated except:

  1. I have to specificy where in the image the axes are

  2. I can't quite get TextRecognize to work on the x-axis ticks.

  3. The zero-height bar at x=0 has to be added manually.

First grab the y-axis centroids

img = Import["https://i.stack.imgur.com/mcL93.jpg"];
yAxis = ImageTake[img, {1, 422}, {1, 150}]

enter image description here

Then find text in the image. Mma sometimes thinks the E is a euro symbol, and sometimes thinks a dot (.) is a comma (,), so I just replace those in case they happen

numStr = 
  StringReplace[
   TextRecognize[yAxis, RecognitionPrior -> "Column"], {"," -> ".", 
    "€" -> "E"}];
numStr = StringReplace[numStr, "E" -> "* 10 ^"];
numStr = StringSplit[numStr, "\n\n"];
nums = ToExpression /@ numStr
(*{0.3, 0.2, 0.1, 0.}*)

Then find the "Centroid" of each y-axis label. Blurring the image helps here:

yAxisBin = yAxis // Binarize // ColorNegate;
yAxisBin = Blur[yAxisBin, 2]
tickHeights = (ComponentMeasurements[mp, "Centroid"] // Values)[[All, 
   2]]
(*{315.338, 230.458, 146.684, 62.1389}*)

![enter image description here

Fit a linear model to the y-tick positions and the y-tick values to get the y-value as a function of vertical pixel coordinate. Also grab the fitted slope as it will be useful later:

yAxVal = LinearModelFit[Thread[{tickHeights, nums}], x, x]
slopeY = yAxVal[[1, 2, 2]]

(*FittedModel[-0.0736902+0.00118571 x]*)
(*0.00118571*)

Unfortunately, I could not get TextRecognize to work with the x-axis, so we will have to hardcode those values in:

xTicks = Range[0, 90, 10];

Now grab the actual bars from the image:

bars = ImageTake[img, {250, 300}, {150, 422}] // Binarize // 
  ColorNegate

enter image description here

Get their bounding boxes, and calculate the pixel height of each bar:

bounds = ComponentMeasurements[bars, "BoundingBox"] // Values;
heights = Last[Flatten[Differences[#]]] & /@ bounds
(*{29., 27., 21., 21., 20., 18., 18., 17., 8.}*)

Note that the bars in ComponentMeasurements are not in left-to-right order, so we need to get the correct ordering for them (by using their horizontal centroid coordinates), and permute the height list to the correct order:

posBars = (ComponentMeasurements[bars, "Centroid"] // Values)[[All, 
    1]];
barIndexes = Ordering@Ordering[posBars];
sorted = Permute[heights, barIndexes];

The bar at x=0 has height 0 and isn't in the plot so I have to manually add a 0 to the start of sorted. Then we will convert the bar pixel heights to y-axis values by using the fitted slope:

PrependTo[sorted, 0];
sorted *= slopeY;
output = AssociationThread[xTicks -> sorted]

And plotting the result looks pretty similar to the original image:

BarChart[output, PlotRange -> {All, {-0.04, 0.3}}, 
 ChartLabels -> Automatic]

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.