Skip to main content

plotting - How to Make a Sankey Diagram


I have two lists


start = {{1},{1},{1},{2},{3},{1}} 

end = {{1},{2},{2},{3},{3},{1}}


And I want to create a Sankey diagram. Which looks something like


enter image description here


So, lines should join the start value to the corresponding end value.


I tried using Graph[] but it didn't work very well - producing this oddly phallic shape.


start = Flatten[start]
end = Flatten[end]

f[x_, y_] := Module[{},
Return[{x <-> y}]]


result = Flatten[MapThread[f, {start, end}]]

Graph[result]

enter image description here



Answer



Here's the start of a SankeyDiagram function:


Options[SankeyDiagram] = Join[
{ColorFunction -> {"Start" -> ColorData[97], "End" -> ColorData["GrayTones"]}},
Options[Graphics]

];

SankeyDiagram[rules_, opts:OptionsPattern[]]:=Module[
{
startcolors, svalues, slens, startsplit,
endcolors, evalues, elens, endsplit,
len, endpos, linecolors
},

len = Length[rules];

endpos = Ordering @ Ordering @ Sort[rules][[All, 2]];

startcolors = OptionValue[ColorFunction->"Start"];
endcolors = OptionValue[ColorFunction->"End"];

{svalues, slens} = Through @ {Map[First], Map[Length]} @ Split[Sort @ rules[[All, 1]]];
startsplit = Accumulate @ Prepend[-slens, len-.5];
linecolors = Flatten @ Table[
ConstantArray[startcolors[i], slens[[i]]],
{i, Length[slens]}

];

{evalues, elens} = Through @ {Map[First], Map[Length]} @ Split[Sort @ rules[[All, 2]]];
endsplit = Accumulate @ Prepend[-elens, len-.5];

Graphics[
{
Table[
{
startcolors[i],

Rectangle[Offset[{-40, 0}, {0, startsplit[[i]]}], Offset[{-10, 0}, {0, startsplit[[i+1]]}]]
},
{i, Length[startsplit]-1}
],
Table[
{
endcolors[(i-1)/(Length[endsplit]-1)],
Rectangle[Offset[{40, 0}, {1, endsplit[[i]]}], Offset[{10, 0}, {1, endsplit[[i+1]]}]]
},
{i, Length[endsplit]-1}

],
Table[
{
White,
Text[
svalues[[i]],
Offset[{-23, 0}, {0, (startsplit[[i]]+startsplit[[i+1]])/2}],
{0, 0},
{0, 1}
]

},
{i, Length[slens]}
],
Table[
{
LightGreen,
Text[
evalues[[i]],
Offset[{23, 0}, {1, (endsplit[[i]]+endsplit[[i+1]])/2}],
{0, 0},

{0, -1}
]
},
{i, Length[elens]}
],
Thickness[.03], Opacity[.7],
Table[
{linecolors[[i]], Line[connector[len-i, len-endpos[[i]]]]},
{i, len}
]

},
opts,
AspectRatio->1
]
]

connector[y1_, y2_] := Table[
{t, y1+(y2-y1) LogisticSigmoid[Rescale[t, {0,1}, {-10,10}]]},
{t, Subdivide[0, 1, 30]}
]


Here is a fair approximation of your desired diagram:


SankeyDiagram[{
1->1,1->2,1->3,1->4,1->5,
2->1,2->2,2->3,2->4,2->5,
3->1,3->2,3->3,3->4,3->5
}]

enter image description here


Comments

Popular posts from this blog

mathematical optimization - Minimizing using indices, error: Part::pkspec1: The expression cannot be used as a part specification

I want to use Minimize where the variables to minimize are indices pointing into an array. Here a MWE that hopefully shows what my problem is. vars = u@# & /@ Range[3]; cons = Flatten@ { Table[(u[j] != #) & /@ vars[[j + 1 ;; -1]], {j, 1, 3 - 1}], 1 vec1 = {1, 2, 3}; vec2 = {1, 2, 3}; Minimize[{Total@((vec1[[#]] - vec2[[u[#]]])^2 & /@ Range[1, 3]), cons}, vars, Integers] The error I get: Part::pkspec1: The expression u[1] cannot be used as a part specification. >> Answer Ok, it seems that one can get around Mathematica trying to evaluate vec2[[u[1]]] too early by using the function Indexed[vec2,u[1]] . The working MWE would then look like the following: vars = u@# & /@ Range[3]; cons = Flatten@{ Table[(u[j] != #) & /@ vars[[j + 1 ;; -1]], {j, 1, 3 - 1}], 1 vec1 = {1, 2, 3}; vec2 = {1, 2, 3}; NMinimize[ {Total@((vec1[[#]] - Indexed[vec2, u[#]])^2 & /@ R...

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...

What is and isn't a valid variable specification for Manipulate?

I have an expression whose terms have arguments (representing subscripts), like this: myExpr = A[0] + V[1,T] I would like to put it inside a Manipulate to see its value as I move around the parameters. (The goal is eventually to plot it wrt one of the variables inside.) However, Mathematica complains when I set V[1,T] as a manipulated variable: Manipulate[Evaluate[myExpr], {A[0], 0, 1}, {V[1, T], 0, 1}] (*Manipulate::vsform: Manipulate argument {V[1,T],0,1} does not have the correct form for a variable specification. >> *) As a workaround, if I get rid of the symbol T inside the argument, it works fine: Manipulate[ Evaluate[myExpr /. T -> 15], {A[0], 0, 1}, {V[1, 15], 0, 1}] Why this behavior? Can anyone point me to the documentation that says what counts as a valid variable? And is there a way to get Manpiulate to accept an expression with a symbolic argument as a variable? Investigations I've done so far: I tried using variableQ from this answer , but it says V[1...