tags:

views:

2767

answers:

9

I have a series of Python tuples representing coordinates:

tuples = [(1,1), (0,1), (1,0), (0,0), (2,1)]

I want to create the following list:

l = []
for t in tuples:
  l[ t[0] ][ t[1] ] = something

I get an IndexError: list index out of range.

My background is in PHP and I expected that in Python you can create lists that start with index > 0, i.e. make gaps and then fill them up, but it seems you can't.

The idea is to have the lists sorted afterwards. I know I can do this with a dictionary, but as far as I know dictionaries cannot be sorted by keys. Update: I now know they can - see the accepted solution.

Edit: What I want to do is to create a 2D array that will represent the matrix described with the tuple coordinates, then iterate it in order. If I use a dictionary, i have no guarantee that iterating over the keys will be in order -> (0,0) (0,1) (0,2) (1,0) (1,1) (1,2) (2,0) (2,1) (2,2)

Can anyone help?

+6  A: 

You should look at dicts for something like that.

for t in tuples:
  if not l.has_key(t[0]):
    l[t[0]] = {}
  l[t[0]][t[1]] = something

Iterating over the dict is a bit different than iterating over a list, though. You'll have the keys(), values() and items() functions to help with that.

EDIT: try something like this for ordering:

for x in sorted(l.keys()):
   for y in sorted(l[x].keys()):
       print l[x][y]
+1: Use the tuple as the index. l[t] = something
S.Lott
Yeah, using the tuple as the index is possible better :)
I'd disagree about using the tuple index - it depends on the expected usage after the dictionary is populated.
Blair Conrad
The question shows the decomposed tuple as a two-part index for nested lists. The two-part list-of-list structure is rarely what you want unless you have a proper matrix; usually you want the tuple as an index.
S.Lott
I do have a proper matrix. there are N*M tuples where N is the highest value of the first tuple member, and M is the highest value of the second tuple member across all tuples.
Discodancer
@Blair Conrad: yes, it depends on the usage, hence me saying "possibly better" - or rather, I said "possible", but that was a typo :P
+8  A: 

No, you cannot create list with gaps. But you can create a dictionary with tuple keys:

tuples = [(1,1), (0,1), (1,0), (0,0), (2,1)]
l = {}
for t in tuples:
    l[t] = something

Update: Try using NumPy, it provides wide range of operations over matrices and array. Cite from free pfd on NumPy available on the site (3.4.3 Flat Iterator indexing): "As mentioned previously, X.flat returns an iterator that will iterate over the entire array (in C-contiguous style with the last index varying the fastest". Looks like what you need.

Rorick
I'd say you can create a list with gaps, but you shouldn't for this case.
Ali A
ok, thanks for the tip, but how can I sort the lists after that ?
Discodancer
Note that this dictionary will be indexable by tuple only - if you're looking to get at somethings with first offset t[0], you're out of luck. It's not clear what your expected usage is, so this may be fine.
Blair Conrad
@dancer: what list?
SilentGhost
To be honest, I don't no how to create lists with gaps. I know that NumPy supports sparse structures. Do you mean that or something different?
Rorick
hm, dict are unsortable by design. what exactly do you mean?
SilentGhost
What I want to do is to create a 2D array that will represent the matrix described with the tuple coordinates, then iterate it in order.If I use a dictionary, i have no guarantee that iterating over the keys will be in order -> (0,0) (0,1) (0,2) (1,0) (1,1) (1,2) (2,0) (2,1) (2,2)
Discodancer
A: 

I think you have only declared a one dimensional list.

I think you declare it as

l = [][]


Edit: That's a syntax error

>>> l = [][]
  File "<stdin>", line 1
    l = [][]
           ^
SyntaxError: invalid syntax
>>>
Dean
That's invalid syntax.
A: 

As mentioned earlier, you can't make lists with gaps, and dictionaries may be the better choice here. The trick is to makes sure that l[t[0]] exists when you put something in position t[1]. For this, I'd use a defaultdict.

import collections
tuples = [(1,1), (0,1), (1,0), (0,0), (2,1)]
l = collections.defaultdict(dict)
for t in tuples:
    l[t[0]][t[1]] = something

Since l is a defaultdict, if l[t[0]] doesn't exist, it will create an empty dict for you to put your something in at position t[1].

Note: this ends up being the same as @unwesen's answer, without the minor tedium of hand-checking for existence of the inner dict. Chalk it up to concurrent answering.

Blair Conrad
+2  A: 

You create a one-dimensional list l and want to use it as a two-dimensional list. Thats why you get an index error.

You have the following options: create a map and use the tuple t as index:

l = {}
l[t] = something

and you will get entries in l as:

{(1, 1): something}

if you want a traditional array structure I'll advise you to look at numpy. With numpy you get n-dimensional arrays with "traditional" indexing.

As I mentioned use numpy,

with numpy you can create a 2-dimensional array, filled with zeros or ones or ... Tha you can fill any desired value with indexing [x,y] as you desire. Of course you can iterate over rows and columns or the whole array as a list.

razong
here is the URL http://www.numpy.org/
razong
+2  A: 

If you know the size that you before hand,you can make a list of lists like this

>>> x = 3
>>> y = 3
>>> l = [[None] * x for i in range(y)]
>>> l
[[None, None, None], [None, None, None], [None, None, None]]

Which you can then iterate like you originally suggested.

Nathan Ross Powell
Or l = [[None] * x] * y
Greg
@Greg, this gives you issues with pointers. When you update l[0][0], it will also update l[1][0] and l[2][0] to be the same value.
tgray
+1  A: 

Extending the Nathan's answer,

tuples = [(1,1), (0,1), (1,0), (0,0), (2,1)]
x = max(tuples, key = lambda z : z[0])[0] + 1
y = max(tuples, key = lambda z : z[1])[1] + 1
l = [[None] * y for i in range(x)]

And then you can do whatever you want

Rodrigo
+1  A: 

What do you mean exactly by "but as far as I know dictionaries cannot be sorted by keys"?

While this is not strictly the same as a "sorted dictionary", you can easily turn a dictionary into a list, sorted by the key, which seems to be what you're after:

>>> tuples = [(1,1), (0,1), (1,0), (0,0), (2,1)]
>>> l = {}
>>> for t in tuples:
...    l[t] = "something"
>>> sorted(l) # equivalent to sorted(l.keys())
[(0, 0), (0, 1), (1, 0), (1, 1), (2, 1)]
>>> sorted(l.items()) # make a list of (key, value) tuples, and sort by key
[((0, 0), 'something'), ((0, 1), 'something'), ((1, 0), 'something'), ((1, 1), 'something'), ((2, 1), 'something')]

(I turned something into the string "something" just to make the code work)

To make use of this for your case however (if I understand it correctly, that is), you would still need to fill the dictionary with None values or something for every "empty" coordinate tuple)

It should have been obvious to me that tuples can be compared and when you sorti a dictionary by keys - which are tuples - you will get the same ordering as if you sort a matrix by rows then by cells :) Thanks.
Discodancer
A: 

The dict solutions given are probably best for most purposes. For your issue of iterating over the keys in order, generally you would instead iterate over the coordinate space, not the dict keys, exactly the same way you would have for your list of lists. Use .get and you can specify the default value to use for the blank cells, or alternatively use "collections.defaultdict" to define a default at dict creation time. eg.

for y in range(10):
    for x in range(10):
        value = mydict.get((x,y), some_default_value)
        # or just "value = mydict[x,y]" if used defaultdict

If you do need an actual list of lists, you can construct it directly as below:

max_x, max_y = map(max, zip(*tuples))
l=[[something if (x,y) in tuples else 0 for y in range(max_y+1)] 
     for x in xrange(max_x+1)]

If the list of tuples is likely to be long, the for performance reasons, you may want to use a set for the lookup,as "(x,y) in tuples" performs a scan of the list, rather than a fast lookup by hash. ie, change the second line to:

tuple_set = set(tuples)
l=[[something if (x,y) in tuple_set else 0 for y in range(max_y+1)] 
     for x in xrange(max_x+1)]
Brian