pms
pms

Reputation: 4626

Converting a list of (x,y,z) tuples spaced on a square lattice to an array

I have a list of tuples e.g. like this:

l=[ (2,2,1), (2,4,0), (2,8,0),
    (4,2,0), (4,4,1), (4,8,0),
    (8,2,0), (8,4,0), (8,8,1) ]

and want to transform it to an numpy array like this (only z values in the matrix, corresponding to the sequence of x, y coordinates, the coordinates should be stored separately) ):

array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])

I'm posting my solution below, but it's pretty low-level and I think there should be some higher-lever solution for this, either using matplotlib or numpy. Any idea?

One needs this kind of conversion to provide the arrays to matplotlib plotting functions like pcolor, imshow, contour.

Upvotes: 0

Views: 1129

Answers (4)

unutbu
unutbu

Reputation: 879849

It looks like np.unique with the return_inverse option fits the bill. For example,

In [203]: l[:,0]
Out[203]: array([2, 2, 2, 4, 4, 4, 8, 8, 8])

In [204]: np.unique(l[:,0], return_inverse = True)
Out[204]: (array([2, 4, 8]), array([0, 0, 0, 1, 1, 1, 2, 2, 2]))

np.unique returns a 2-tuple. The first array in the 2-tuple is an array of all the unique values in l[:,0]. The second array is the index values associating values in array([2, 4, 8]) with values in the original array l[:,0]. It also happens to be the rank, since np.unique returns the unique values in sorted order.


import numpy as np
import matplotlib.pyplot as plt

l = np.array([ (2,2,1), (2,4,0), (2,8,0),
               (4,2,0), (4,4,1), (4,8,0),
               (8,2,0), (8,4,0), (8,8,1) ])

x, xrank = np.unique(l[:,0], return_inverse = True)
y, yrank = np.unique(l[:,1], return_inverse = True)

a = np.zeros((max(xrank)+1, max(yrank)+1))
a[xrank,yrank] = l[:,2]

fig = plt.figure()
ax = plt.subplot(111)

ax.pcolor(x, y, a)   
plt.show()

yields

enter image description here

Upvotes: 2

EnricoGiampieri
EnricoGiampieri

Reputation: 6085

a solution using standard python construct set, list and sorted. if you don't have a lot of pointsit gains in readability even if slower than the numpy solution given by unutbu

l=[ (2,2,1), (2,4,0), (2,8,0),
    (4,2,0), (4,4,1), (4,8,0),
    (8,2,0), (8,4,0), (8,8,1) ]

#get the ranks of the values for x and y
xi = sorted(list(set( i[0] for i in l )))
yi = sorted(list(set( i[1] for i in l )))
a = np.zeros((len(xi),len(yi)))
#fill the matrix using the list.index
for x,y,v in l:
    a[xi.index(x),yi.index(y)]=v

ax=plt.subplot(111)
ax.pcolor(array(xi), array(yi), a)

Upvotes: 0

Eric
Eric

Reputation: 97601

I don't understand why you're making this so complex. You can do it simply with:

array([
    [cell[2] for cell in row] for row in zip(*[iter(x)] * 3)
])

Or perhaps more readably:

array([
    [a[2], b[2], c[2]] for a, b, c in zip(x[0::3], x[1::3], x[2::3])
])

Upvotes: 0

pms
pms

Reputation: 4626

My solution first ranks the x and y values, and then creates the array.

l=[ (2,2,1), (2,4,0), (2,8,0),
    (4,2,0), (4,4,1), (4,8,0),
    (8,2,0), (8,4,0), (8,8,1) ]

def rankdata_ignoretied(data):
   """ranks data counting all tied values as one"""
   # first translate the data values to integeres in increasing order
   counter=0
   encountered=dict()
   for e in sorted(data):
      if e not in encountered:
         encountered[e]=counter
         counter+=1   
   # then map the original sequence of the data values
   result=[encountered[e] for e in data]
   return result

x=[e[0] for e in l]
y=[e[1] for e in l]
z=[e[2] for e in l]

xrank=rankdata_ignoretied(x)
yrank=rankdata_ignoretied(y)

import numpy
a=numpy.zeros((max(xrank)+1, max(yrank)+1))
for i in range(len(l)):
   a[xrank[i],yrank[i]]=l[i][2]

To use the resulting array for plotting one also needs the original x and y values, e.g.:

ax=plt.subplot(511)
ax.pcolor(sorted(set(x)), sorted(set(y)), a)

Anyone has a better idea of how to achieve this?

Upvotes: 0

Related Questions