Skip to main content

graphics - AspectRatio option works unpredictably?


Let us examine the agreement between specified value for the AspectRatio option and the actual aspect ratio of generated plot. For this purpose I define a function which uses the Rasterize trick for determination of the actual aspect ratio:



realAspectRatio = #2/#1 & @@ (#2 - #1) & @@ 
Rasterize[
Show[#, Epilog -> {Annotation[
Rectangle[ImageScaled[{0, 0}], ImageScaled[{1, 1}]], "Two",
"Region"]}], "Regions"][[-1, 2]] &;

(Instead of realAspectRatio@ one can use much more basic but lesser precise function #2/#1 & @@ ImageDimensions@ what gives the same results.)


At first, we check the agreement for simple graphics Graphics[{Point[{{0, 0}, {1, 1}}]}]:


table = Table[{10^p, realAspectRatio@
Graphics[{Point[{{0, 0}, {1, 1}}]}, AspectRatio -> 10^p]}, {p, -2, 2, .1}];

lm = LinearModelFit[table, x, x];
lm["BestFit"]
ListPlot[lm["FitResiduals"], Frame -> True,
FrameLabel -> {"AspectRatio", "fit residual"}]


1.26421 + 0.603143 x


plot



One can see that actual aspect ratio substantially differs from the requested via the AspectRatio option and the dependence between them is nonlinear!



In this case the situation becomes MUCH better if we provide at least horizontal PlotRange specification PlotRange -> {{0, 1}, Automatic}:


table = Table[{10^p, 
realAspectRatio@
Graphics[{Point[{{0, 0}, {1, 10}}]}, AspectRatio -> 10^p,
PlotRange -> {{0, 1}, Automatic}]}, {p, -2, 2, .1}];
lm = LinearModelFit[table, x, x];
lm["BestFit"]
ListPlot[lm["FitResiduals"], Frame -> True,
FrameLabel -> {"AspectRatio", "fit residual"}]



0.00391481 + 1.00225 x


plot



But for a little more complex graphics ListPlot[Prime[Range[25]]] this remedy does not help even if we vary AspectRatio in much lesser diapason from 1/5 to 5:


table = Table[{ar, realAspectRatio@
ListPlot[Prime[Range[25]], PlotRange -> {{0, 25}, {0, 100}},
AspectRatio -> ar]}, {ar, 1/5, 5, .1}];
lm = LinearModelFit[table, x, x];
lm["BestFit"]

ListPlot[lm["FitResiduals"], Frame -> True,
FrameLabel -> {"AspectRatio", "fit residual"}]


0.248369 + 0.768459 x


plot



How this behavior can be explained? Is it intended behavior? And more importantly: is there a way to make the actual aspect ratio predictable?




Summary of the discussion and the conclusion



The source of confusion was the first sentence under the "Details" section on the Documentation page for AspectRatio: "AspectRatio determines the scaling for the final image shape." This statement is clearly wrong and more correct statement can be found as the first point under the "Properties and Relations" subsection in the "Examples" section on the same page: "AspectRatio determines the ratio of PlotRange, not ImageSize." But as it is clearly demonstrated in dedicated answer, even this statement is not precise because actually AspectRatio determines the aspect ratio of the plotting area which is specified by both PlotRange and PlotRangePadding.



Answer



It seems to me that there is little point in making the image size so small that the image cannot possibly have a close to accurate aspect ratio. If I also get rid of the superfluous parts outside of the plot range by setting PlotRangePadding, ImagePadding, and ImageMargins all to zero, I can use simple ImageDimensions to check aspect ratio. Doing all this I find that it is quite precise in 10.1 under Windows:


table = Table[{10^p, 
N[#2/#] & @@
ImageDimensions@
Image@Graphics[{Disk[]}, AspectRatio -> 10^p, PlotRangePadding -> 0,
ImagePadding -> 0, ImageMargins -> 0, ImageSize -> {2000}]}, {p, -2, 2, .1}];

lm = LinearModelFit[table, x, x];

lm["BestFit"]
ListPlot[lm["FitResiduals"], Frame -> True, FrameLabel -> {"AspectRatio", "fit residual"},
DataRange -> {10^-2, 10^2}, PlotRange -> All]


-0.00279057 + 1.00046 x

enter image description here


Likewise your ListPlot example:


table = Table[{ar, 

N[#2/#] & @@
ImageDimensions@
Image@ListPlot[Prime[Range[25]], PlotRange -> {{0, 25}, {0, 100}},
AspectRatio -> ar, PlotRangePadding -> 0, ImagePadding -> 0, ImageMargins -> 0,
ImageSize -> {2000}]}, {ar, 1/5, 5, .1}];

lm = LinearModelFit[table, x, x];
lm["BestFit"]
ListPlot[lm["FitResiduals"], Frame -> True, FrameLabel -> {"AspectRatio", "fit residual"},
DataRange -> {1/5, 5}]



0.000344083 + 0.999798 x

enter image description here


Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

equation solving - Invert and fit implicitly defined curve

I need to fit an implicitly defined curve. I thought I could get some data out of Solve , and then using FindFit . Therefore, I would like to find the relation the parametric curve defined by $F(x,y)=0$: Solve[-(1/2) + 1/2 (0.41202 BesselK[0, 0.1 Sqrt[x^2 + y^2]] + (0.101483 x BesselK[1, 0.1 Sqrt[x^2 + y^2]])/Sqrt[x^2 + y^2]) == 0, y] But I can't get an output: Solve was unable to solve the system with inexact coefficients or the system obtained by direct rationalization of inexact numbers present in the system. Since many of the methods used by Solve require exact input, providing Solve with an exact version of the system may help. >> Edit: In particular, I would like to fit the data coming from the curve with the expression of another curve, and not with a function $f(x)$. In particular, since this clearly looks like a cardioid , I would like it to fit to something like it. What other strategies could I try?

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...