17

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data.

I would now like to show these different 2D plots projected in 3D as in enter image description here

but then using proper perspective.

In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes.

Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage).

(* some test data *)
data = Table[Null, {i, 4}];
data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 
   4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 
   1.0}, {1, -6.8}, {2, -4.7}, {3, -1.0}, {4, 0.03}, {5, 
   2.8}};  (* data on logit(proportion) scale *)
data[[2]] = ((data[[1]] // Transpose))*{1, 0.8} // Transpose; 
data[[3]] = ((data[[1]] // Transpose))*{1, 0.6} // Transpose; 
data[[4]] = ((data[[1]] // Transpose))*{1, 0.4} // 
  Transpose; (* data points groups 1-4 on logit(proportion) scale *)
Logit[p_] = Log[p/(1 - p)];
Invlogit[x_] = Exp[x]/(1 + Exp[x]);
datalog = Table[Null, {i, 4}];
Do[datalog[[i]] = 
   Partition[
    Riffle[(data[[i]] // Transpose)[[1]], 
     Log10[100*Invlogit[(data[[i]] // Transpose)[[2]]]]], 2], {i, 1, 
   4}];

(* fit function plus best fit & conf lims *)
fitfunc[t_, z_, Z_, p0_, s_] := 
  z + (p0 (z - Z))/(-p0 + (-1 + p0) (1/(1 + s))^t);
predicted = 
  Table[NonlinearModelFit[
    dat[[i]], {fitfunc[t, z, Z, p0, s]}, {{Z, 0.46}, {s, 20}, {p0, 
      0.0005}, {z, -6.4}}, t, MaxIterations -> 1000, 
    Method -> "LevenbergMarquardt"], {i, 4}];
pred[t_, i_] := Normal[predicted[[i]]];
conflims[t_, i_] := predicted[[i]]["MeanPredictionBands"];
Tom Wenseleers
  • 897
  • 5
  • 16
  • 4
    This is likely doable. However, I'm sure this is also one of the things that would give Tufte quite a fit; you could do just as well with all three ribbon plots in two dimensions, perhaps in separate plots if combining them proves interfering. – J. M.'s missing motivation Jun 11 '15 at 21:41
  • Well I tried multipanel plots before - it's just that in the article where I would like to use this I will not have the space to do this (I have 5 groups, which would be 5 panels, and the curves and points also overlap too much to be able to overlay them in one 2D graph, even when using transparency). So the 3D way that I show would be the only way to combine everything within the space that I have. – Tom Wenseleers Jun 12 '15 at 05:12
  • 1
    And to be able to read the Y values more clearly I would still like to add Y grid lines at the back and maybe invert the X scale so that the high Y values would be at the back (it's only the values at the endpoint that matter really for me). – Tom Wenseleers Jun 12 '15 at 09:02
  • Instead of assembling 2d to 3d, one can fit directly to 3d with a curvature in third axis direction. – Narasimham Jun 14 '15 at 04:53
  • 1
    Ha no but the different groups are not logically ordered, so it wouldn't make sense to also do a fit in the 3d axis direction - it's separate categorical groups really that I want to show next to each other – Tom Wenseleers Jun 14 '15 at 07:16

1 Answers1

22

You could make 2d plot and then convert 2d coord to 3d:

data1 = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 
    4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 
    1.0}, {1, -6.8}, {2, -4.7}, {3, -1.0}, {4, 0.03}, {5, 2.8}};

data = Table[{1, 1.2 - j*.2} i, {j, 4}, {i, data1}];

Logit[p_] = Log[p/(1 - p)];
Invlogit[x_] = Exp[x]/(1 + Exp[x]);
datalog = Table[Null, {i, 4}];

datalog = 
  Table[Transpose[{#1, Log10[100 Invlogit[#2]]} & @@ 
     Transpose[data[[i]]]], {i, 1, 4}];

fitfunc[t_, z_, Z_, p0_, s_] := 
  z + (p0 (z - Z))/(-p0 + (-1 + p0) (1/(1 + s))^t);

predicted = 
  Table[NonlinearModelFit[
    data[[i]], {fitfunc[t, z, Z, p0, s], 
     Z > z && Z < 5 && s > 1 && s < 200 && p0 < 0.01 && p0 > 0 && 
      z < -2 && z > -7}, {{Z, 0.3}, {s, 20}, {p0, 0.001}, {z, -6.5}}, 
    t, MaxIterations -> 1000], {i, 4}];

pred[t_, i_] := Normal[predicted[[i]]];
conflims[t_, i_] := predicted[[i]]["MeanPredictionBands"];

cols = {Red, Darker[Green], Blue, Darker[Cyan]};

opac = 0.4;
colslight = {Directive[cols[[1]], Opacity[opac]], 
   Directive[cols[[2]], Opacity[opac]], 
   Directive[cols[[3]], Opacity[opac]], 
   Directive[cols[[4]], Opacity[opac]]};

Graphics3D[
 Table[{Plot[{Log10[100*Invlogit[pred[t, i]]], 
       Evaluate[N@Log10[100*Invlogit[conflims[t, i]]]]}, {t, 1, 5}, 
      Filling -> {2 -> {{1}, {colslight[[i]]}}, 
        3 -> {{1}, {colslight[[i]]}}}, 
      PlotStyle -> {cols[[i]], colslight[[i]], 
        colslight[[i]]}][[1]], {cols[[i]], PointSize[0.012], 
     Point[datalog[[i]]]}} /. {x_?NumericQ, y_?NumericQ} :> {x, i, 
     y}, {i, 1, 4}], 
    Axes -> {True, False, True}, 
    Boxed -> {Right, Bottom, Back}, 
    BoxRatios -> {1, 1, 0.5}, 
    FaceGrids -> {{0, 0, -1}, {0, 1, 0}, {1, 0, 0}}, 
    FaceGridsStyle -> 
  Directive[GrayLevel[0.3, 1], AbsoluteDashing[{1, 2}]],
 ViewPoint -> {-2, -2.5, 1}, 
 AxesLabel -> {"Day", "", 
   Rotate[Row[{Spacer[50], "Resistance (%)"}], 90 Degree]},
 LabelStyle -> Directive[Black, Bold, 14],
 ImageSize -> 500
   ]

enter image description here

halmir
  • 15,082
  • 37
  • 53
  • Many thanks for this! Brilliant! And how would you change the points to means and 95% conf lims? – Tom Wenseleers Jun 14 '15 at 06:28
  • also one other question: if I wanted to represent the points using sphereas and the 95% conf lims using tubes - would you know how to do that? Replacing Point with Sphere[datlog[[i]], 0.07] gets close for the first part - but the aspect ratio of the balls is incorrect then... – Tom Wenseleers Jun 15 '15 at 08:55
  • Or would using Circulsphere[] work to get the aspect ratio right? – Tom Wenseleers Jun 15 '15 at 10:29
  • or you could set BoxRatios -> {1, 1, 1} if you don't mind – halmir Jun 15 '15 at 13:34
  • Yes that's true, but I kind of liked the current aspect ratio. Maybe with some code given at http://mathematica.stackexchange.com/questions/10999/how-to-plot-error-bars-in-a-3d-scatter-plot I could get it to work.... – Tom Wenseleers Jun 15 '15 at 15:52
  • then try GeometricTransformation[Sphere[#, .08], ScalingTransform[{.5, .5, 1}, #]] & /@ (datalog[[i]] /. {x_?NumericQ, y_?NumericQ} :> {x, i, y}) instead of Point[datalog[[i]]] – halmir Jun 15 '15 at 16:36
  • Thx for that - with ScalingTransform[{1.5, 0.5, 2}, #] I get approx round spheres! Not sure where the values are coming from though - I have BoxRatios -> {1, 1, 0.5} and PlotRange -> {{1, 10}, {1, 4}, {-4, 2.2}} – Tom Wenseleers Jun 15 '15 at 17:11
  • And would you know by any chance how to replace the points with the means for each time points plus the standard errors on the means? (sorry promise this is my last question) – Tom Wenseleers Jun 15 '15 at 17:12
  • datalog is the point. You could replace that with whatever you want – halmir Jun 15 '15 at 17:18
  • Yes I realise that - my initial problem was more how to calculate the mean and SEs for each combination of series i and a given day, and then to add the error bars using one of the recipies here, http://mathematica.stackexchange.com/questions/3897/plotting-error-bars-on-a-log-scale - sorry, I'm a beginning Mathematica user as you can tell! – Tom Wenseleers Jun 15 '15 at 17:25