Skip to main content

graphics - Subplots with connector lines


I am looking for advice from people who have more experience in this area on what is the best (simplest, least effort) way to create a graphic like the following:


Mathematica graphics


This is a rough mockup made in a drawing program. There is a central graph, surrounded by smaller ones, each of which is showing some information about a point in the main graph. Those points are connected to the subgraphs with lines.


Requirements:





  • Each plot must be able to have their own axes/frame




  • Proper alignments of the connector lines (red dashed lines on the mockup)---I have the coordinates of one end in the coordinate system of the central plot, while the other end must point at the smaller plots.




  • Consistent font sizes and line widths (i.e. everything must be 8 pt when printed)





  • Vector graphics (I'd like to avoid rasterizing to bitmaps)




Possible approaches:




  • GraphicsGrid with Epilog (GraphicsGrid seems to be based on Inset.)





  • Lots of Insets in a graphic (the main issue is aligning the coordinate system of the central plot with that of the whole graphic)




  • Learn to use LevelScheme (I didn't use it for anything serious yet, but when I tried it last time it seemed to have issued with alignment).




Whenever I start doing something like this, and the details must be accurate, lots of small issues tend to come up. I'd like to know which approach is likely to prove the least troublesome.




Answers summary


The main difficulty was the correct positioning of connector lines. The usual way of including subplots is by using Inset (which is also used by GraphicsGrid). One endpoint of the lines is in the main graphics coordinate system, while the other is in the central subplot coordinate system. Converting between the two is very difficult and depends on the scaling of graphics.



Heike's solution uses FullGraphics to expand the axes/frames of subplots. Then all subplots can be directly included in the main graphic and scaled to size. There will be a single coordinate system to deal with.


Chris Degnen's solution uses image processing to align the main graphic coordinate system with the inset coordinate system. It places a red dot at the desired endpoints, rasterizes the graphic, measures the position of the dot, and then uses this information to compose a vector graphic with the connector lines going between these positions. The result is a vector graphic that looks correct only at a certain scale, but can be exported to PDF.


The other solutions recommend adding the connector lines manually.



Answer



This solution uses FullGraphics to transform axes and ticks in a plot to lines which allows you to resize and translate the plot while keeping the ticks of the original plot. In raster, main is the main plot, list is the list of sub plots, pts is the list of points in the main plot corresponding to the begin points of the red lines, and {dx, dy} are the gaps between the sub plots and the main plot. The sub plots are placed in clockwise direction starting with the one in the upper right corner. The end plot is such that the plot range of the main plot is {{0, 0}, {1, 1}}.


raster[main_, list_, pts_, {dx_, dy_}] := 
Module[{fgmain, fglist, prm, prl, scmain, sclist, scpts, lines},
fgmain = FullGraphics[main];
fglist = FullGraphics /@ list;
prm = OptionValue[AbsoluteOptions[main, PlotRange][[1]],

PlotRange];
prl = OptionValue[Options[#, PlotRange][[1]], PlotRange] & /@ list;
scmain =
Translate[
Scale[fgmain[[1]], 1/(prm[[All, 2]] - prm[[All, 1]]),
prm[[All, 1]]], -prm[[All, 1]]];
scpts = Transpose[{Rescale[pts[[All, 1]], prm[[1]]],
Rescale[pts[[All, 2]], prm[[2]]]}];
sclist = MapThread[
Translate[

Scale[#, (.5 - {dx, dy}/
2)/(#2[[All, 2]] - #2[[All, 1]]), #2[[All, 1]]],
-#2[[All, 1]] + #3] &,
{fglist[[All, 1]], prl, {{-.5 - dx/2, 1 + dy},
{0, 1 + dy}, {.5 + dx/2, 1 + dy}, {1 + dx, 1 + dy},
{1 + dx, .5 + dy/2}, {1 + dx, 0}, {1 + dx, -.5 - dy/2},
{.5 + dx/2, -.5 - dy/2}, {0, -.5 - dy/2}, {-.5 - dx/2, -.5 -
dy/2},
{-.5 - dx/2, 0}, {-.5 - dx/2, .5 + dy/2}}}];
lines = Transpose[{scpts,

{{-dx, 1 + dy}, {.25 - dx/4, 1 + dy}, {.75 + dx/4,
1 + dy}, {1 + dx, 1 + dy},
{1 + dx, .75 + dy/4}, {1 + dx, .25 - dx/4}, {1 + dx, -dy},
{.75 + dx/4, -dy}, {.25 - dx/4, -dy}, {-dx, -dy},
{-dx, .25 - dy/4}, {-dx, .75 + dy/4}}}];
Graphics[{scmain, sclist, {Red, Dashed, Line[lines]}}]]

Example:


list = MapIndexed[ParametricPlot[#, {x, 0, 2 Pi}, 
Frame -> True, Axes -> False,

PlotStyle -> (ColorData[1] @@ #2)] &,
Table[{(n - 1) 2 Pi + x, n Sin[x]}, {n, 12}]];
main = Show[list, PlotRange -> All];
pts = N[Table[{(n - 1) 2 Pi + x, n Sin[x]}, {n, 12}] /. x -> Pi];

raster[main, list, pts, {.15, .15}]

Mathematica graphics


Comments

Popular posts from this blog

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...