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$
1
  • $\begingroup$ Your manual version has eight spheres, while the matrix only has seven non-null entries. ;) $\endgroup$
    – Jinxed
    Commented Mar 11, 2015 at 12:26

2 Answers 2

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$
2
  • $\begingroup$ Brilliant - cheers! $\endgroup$
    – DRG
    Commented Mar 11, 2015 at 18:42
  • $\begingroup$ @DRG: Glad to hear to have been of some help! $\endgroup$
    – Jinxed
    Commented Mar 11, 2015 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$
2
  • $\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
    Commented Mar 11, 2015 at 13:29
  • $\begingroup$ Great answer, will use this technique for something else I have in mind! Thanks $\endgroup$
    – DRG
    Commented Mar 11, 2015 at 18:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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