2
$\begingroup$

I have a grid of values generated from a MATLAB evaluation. Most of these elements are 0, some 0.5 and the remainder 1. I'd like to write a mathematica script which reads in the CSV and then, in every position where there is a 1, I'd like it to create a Graphics3D red sphere of radius 1, and in every position where there is an 0.5, a green sphere of radius 1. However, I'm not sure whether the best approach is to use loops or something more natural in Mathematica. To illustrate, here's a sample 5 x 5 matrix;

M = {{0, 0, 0, 0, 0.5}, {1, 0, 0, 0, .5}, {0, 0, 0, 0, 0.5}, {1, 0,
 1, 0, 0}, {0, 0, 0, 0, 1}};

I can get all the ones and halfs from the grid as follows;

Ones = Position[M, 1];
Halfs = Position[M, 0.5];
OneLength = Length[Ones];
HalfLength = Length[Halfs];
Xpos = Part[Part[Ones, 1], 1];
Ypos = Part[Part[Ones, 1], 2];

Where I get the lengths in case I need to run a loop - which I'm trying to avoid. Similarly the Xpos and Ypos give a co-ordinate I could loop but I'm sure there's an easier way. In essence, I want to end up with a picture like this for the sample data above;

enter image description here

In the example above, I generated it manually using

j1 = Graphics3D[{Opacity[0.5], Green, Sphere[{5, 6 - 1, 0}]}];
j2 = Graphics3D[{Opacity[0.5], Green, Sphere[{5, 6 - 2, 0}]}];
j3 = Graphics3D[{Opacity[0.5], Green, Sphere[{5, 6 - 3, 0}]}];
j4 = Graphics3D[{Opacity[0.5], Green, Sphere[{4, 6 - 5, 0}]}];
g1 = Graphics3D[{Opacity[0.5], Red, Sphere[{1, 6 - 2, 0}]}];
g2 = Graphics3D[{Opacity[0.5], Red, Sphere[{1, 6 - 4, 0}]}];
g3 = Graphics3D[{Opacity[0.5], Red, Sphere[{3, 6 - 4, 0}]}];
g4 = Graphics3D[{Opacity[0.5], Red, Sphere[{5, 6 - 5, 0}]}];
Show[j1, j2, j3, j4, g1, g2, g3, g4]

Notice that I've subtracted the y element from 6, as Matlab starts its y-count from the top down rather than from the axis, so any matrix I get in from Matlab will need something like this to Y-flip. Is there a clever way to automate this, using table or otherwise which will circumvent loops? If loops are required, what is the most efficient way of creating one? I will eventually be working with 200 x 200 arrays so manual manipulation would be best avoided! Thanks in advance...

$\endgroup$
  • $\begingroup$ Your manual version has eight spheres, while the matrix only has seven non-null entries. ;) $\endgroup$ – Jinxed Mar 11 '15 at 12:26
3
$\begingroup$

Using Map, you can avoid looping:

Graphics3D[{Opacity[0.5],
 {Red, Sphere /@ (Position[M, 1] /. {x_, y_} :> {x, 6 - y, 0})},
 {Green, Sphere /@ (Position[M, .5] /. {x_, y_} :> {x, 6 - y, 0})}}]

or a little shorter (but more nerdy):

Graphics3D[{Opacity[0.5],
 {#1,Sphere/@#2}&@@{#[[2]],Position[M,#[[1]]]/.{x_,y_}:>{x,6-y,0}}&/@{{1,Red},{0.5,Green}}}]

With some more options, you get your desired 3D-view:

Graphics3D[{Opacity[0.5],
 {#1,Sphere/@#2}&@@{#[[2]],Position[M,#[[1]]]/.{x_,y_}:>{x,6-y,0}}&/@{{1,Red},{0.5,Green}}},
 ViewPoint->Above,ViewVertical->{0,0,1},Boxed->False,AxesOrigin->{0,0,0},
 Axes->{True,True,False},AxesLabel->{"x","y","z"}]

3D-view

$\endgroup$
  • $\begingroup$ Brilliant - cheers! $\endgroup$ – DRG Mar 11 '15 at 18:42
  • $\begingroup$ @DRG: Glad to hear to have been of some help! $\endgroup$ – Jinxed Mar 11 '15 at 20:40
6
$\begingroup$

The MapIndexed function works very nicely for this, as it provides both the value of the array element, and a list representing its position. I wasn't golfing here, so I used pattern matching on function arguments for the rest:

draw[0, _] = {};
draw[v : Except[0], {x_, y_}] :=
  {v /. {0.5 -> Green, 1 -> Red}, 
   Sphere[{x, 6 - y, 0}]};

Graphics3D[{
  Opacity[0.5],
  MapIndexed[draw, M, {2}]}]
$\endgroup$
  • $\begingroup$ +1. MapIndexed seems the most appropriate tool. I might use Condition with numeric tests, esp. since the data is coming from outside Mathematica. E.g. draw[zero_, _] /; zero == 0 :=... and v /. {v0_ /; v == 0.5 -> Green, v0_ /; v == 1 -> Red}. (For instance 1 matches only the Integer and not the Real number 1, and the numbers might be off by machine epsilon or so. Using Equal allows for such differences.) $\endgroup$ – Michael E2 Mar 11 '15 at 13:29
  • $\begingroup$ Great answer, will use this technique for something else I have in mind! Thanks $\endgroup$ – DRG Mar 11 '15 at 18:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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