1

Suppose I have an array X of x values and Y of y values, and a dictionary results, such that results[x][y] accesses a z value. How would I construct a z variable do correctly match the coordinates that are generating it?

For instance:

X = [0,2,3]
Y = [-1,-2,-3]
results = {x:{y: x/y for y in Y} for x in X}

Z = np.array([[results[x][y] for y in Y] for x in X ])
fig = go.Figure(data=[go.Surface(z=Z, x=X, y=Y)])
fig.update_layout(title='Plot', autosize=False,
                  width=500, height=500,
                  margin=dict(l=65, r=50, b=65, t=90))
fig.update_layout(scene = dict(xaxis_title='X',
                    yaxis_title='Y'
                              )
                 )
fig.show()

gives a wrong graph:

enter image description here

How does this coordinate system work? Doing Z = np.array([[results[x][y] for x in X] for y in Y ]) gives the correct result, but this doesn't make sense to me. Why is x the second coordinate?

0

1 Answer 1

2

The problem

The reason you're getting weird results is due to how computers represent matrices, which is by rows and columns where the "first" element, at the index 0, 0 is at the top-left corner, whereas it's more intuitive to be thinking of the XY-plane where bottom-left corner is the origin, which is the standard way to view graphs in mathematics.

Solution

First, by taking full advantage of numpy, you can compute z much quicker via np.divide.outer(). This gives the correct result for z without the unnecessary dict and nested comprehension (and will be WAY more efficient for larger arrays).

x = np.array([0, 2, 3])
y = np.array([-1, -2, -3])
z = np.divide.outer(x, y)

Now since computers see matrices differently than what is intuitively viewed as z = f(x, y), you need to plot the transpose of z:

fig = go.Figure(data=[go.Surface(z=z.T, x=x, y=y)])

Result:

Slightly more detailed explanation

If we orient the plot such that we're looking at the x-axis oriented how we normally view it on 2D graphs (increasing from left to right):

You can see that on the left edge of the surface, z, all the values are zero. But notice how z is represented as an array:

>>> np.round(z, 2)
array([[-0.  , -0.  , -0.  ],
       [-2.  , -1.  , -0.67],
       [-3.  , -1.5 , -1.  ]])

If you transpose z, you'll see that the "left" edge of the matrix is all zeros:

>>> np.round(z.T, 2)
array([[-0.  , -2.  , -3.  ],
       [-0.  , -1.  , -1.5 ],
       [-0.  , -0.67, -1.  ]])

This hopefully helps shine some light on the difference between xy-coordinates and the row-column representation of arrays. Something else to note is that the y-axis is "reversed" in the reoriented plot above. This is because the top row of a matrix is considered the "first" element, but in a normal xy-coordinate system, the y-axis increases going vertically (i.e., starting at the bottom).

Using a meshgrid for multivariate functions

Something you can do to avoid having to transpose z is using np.meshgrid():

>>> x_coords, y_coords = np.meshgrid(x, y, indexing="xy")
>>> z = x_coords / y_coords
>>> z
array([[-0.  , -2.  , -3.  ],
       [-0.  , -1.  , -1.5 ],
       [-0.  , -0.67, -1.  ]])

Notice now that z is already in the proper format for plotting as a surface. Note that this also gets rid of the necessity of np.outer(). Using a meshgrid is also much better for evaluating arbitrary functions on a coordinate grid.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the detailed explanation. You explained the problem clearly by pointing out the way the data is interpreted by the machine. I was actually aware of the broadcasting capabilities of NumPy and how they could circumvent this issue. However, in my real-world application, there is no possibility for vectorizing/broadcasting, and the data needs to be accessed by that dictionary.
Did my answer solve your problem? If so, please go ahead and accept it so that your question can be removed from the unanswered queue! Glad I could help.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.