2

I am trying to store the attributes of a shapefile into a list. With following code it works for smaller shapes:

addA = []
While provider.nextFeature(feat):
    attrs = feat.attributeMap()
    for (k,attr) in attrs.iteritems():
        addA.append(attr.toString())

If I try to store a large shape my pc runs out of memory or freezes. I tried to put the attributes in a blist and in a numpy array, but nothing really worked (or probably I did not understand how to set up a proper numpy array...). Is there a solution for this problem?

5
  • Why do you need to put it on a list btw? Maybe there would be some solutions that won't require you to put it on a list.
    – R.K.
    Commented May 27, 2013 at 12:19
  • i want to do a comparisson between the attributes of two shapes. Therfore i want to save the attributes in a list and compare the lists. It is possible to select the attributes for certain fields, that only the requireded attributes are stored in the list. But this step freezes qgis as well.
    – user18550
    Commented May 27, 2013 at 13:25
  • Only the attributes change then? The geometries are constant?
    – R.K.
    Commented May 27, 2013 at 14:37
  • I need only the attributes. The geometries are constant.
    – user18550
    Commented May 27, 2013 at 14:56
  • I iterated over all the attributes it counts 13050810 with a time of 39,92 sec. Probably the data is too big to store into a normal list?
    – user18550
    Commented May 27, 2013 at 15:05

1 Answer 1

3

It is not a problem of numpy arrays or other things, it's a problem of garbage collection. Each time you create a list, everything is stored in memory and if the list is big.... So you need to know Python (and not only PyQGIS) and the tricks to get out ("big data problem", you should reduce memory usage and use algorithms that do not slow down the process):

First: do not use a while loop (see While loop):

"While loops, like the For Loop, are used for repeating sections of code - but unlike a for loop, the while loop will not run n times, but until a defined condition is met. As the for loop in Python is so powerful, while is rarely used, except in cases where a user's input is required"

which is not the case here and it slows things due to first testing the True condition every loop.

If you want to process all data in a layer in QGIS 1.8 (not necessary in master version which allows any process, even if the elements of a layer are not selected).

def select_all(layer):
    # select all elements of a layer, geometry and attributes
    layer.select([])
    layer.setSelectedFeatures([obj.id() for obj in layer])

layer = qgis.utils.iface.activeLayer()
select_all(layer)

And after

for elem in layer.selectedFeatures():
    geom= elem.geometry()
    attrs = elem.attributeMap()

and no more while loop !

Secondly, if you only need the values of a dictionary, uses attrs.values() and not attrs.iteritems(), forcing the loop to go through all keys and values

addA = []
for elem in layer.selectedFeatures():
    attrs = elem.attributeMap()
    for  value in attrs.values():
        addA.append(value.toString())

Thirdly, you can use iterators, generators or list comprehension, see Python Generator Hacking:

List comprehension (can be used anywhere a sequence is expected)

what it means:

result = []
for x in s:
if condition:
    result.append(expression)

so:

addA = []
for elem in layer.selectedFeatures():
    attrs = elem.attributeMap()
    addA.append([value.toString() for value in attrs.values()])

and if you want, you can construct a nested list comprehension for addA in one line (without using a preliminary empty list, saving memory):

addA = [[value.toString() for value in elem.attributeMap().values()] for elem in macouche.selectedFeatures()]

"This means that list comprehensions aren’t useful if you’re working with iterators that return an infinite stream or a very large amount of data. Generator expressions are preferable in these situations" (functional Python)

with Generators:

A generator is a one-time operation. It does not construct a list and you can iterate over the generated data once, but if you want to do it again, you have to call the generator function again. Once consumed, it disappears from memory.

what it means (no list, return one value):

for x in s: 
   if condition:
       yield expression

so:

addA = []
for elem in layer.selectedFeatures():
     attrs = elem.attributeMap()
     generator =(value.toString() for value in attrs.values())
     print generator

 <generator object <genexpr> at 0x12bff6eb0>
  ....

Its purpose is only iteration:

addA = []
for elem in layer.selectedFeatures():
    attrs = elem.attributeMap()
    generator =(value.toString() for value in attrs.values())
    for i in generator:
        addA.append(i)

and with a huge savings in memory area:

addA = []
# generator
for i in ((value.toString() for value in elem.attributeMap().values()) for elem in  macouche.selectedFeatures()):
    for j in i:
         addA.append(j)

I hope this will help

4
  • You shouldn't use setSelectedFeatures unless you want to highlight them in the map. You only need layer.select()
    – Nathan W
    Commented May 28, 2013 at 0:08
  • FYI this is how you should do it gist.github.com/NathanW2/5659716 You are also not going to get good performance out of selectedFeatures() as that is returning a full list. (github.com/qgis/Quantum-GIS/blob/master/src/core/…).
    – Nathan W
    Commented May 28, 2013 at 0:21
  • I'm sorry, but your solution (gist) does not work for me (in 1.8, empty list as result), without selecting the features first ( full list as result). I know the the 2.0 solution
    – gene
    Commented May 28, 2013 at 6:38
  • Works for me in 1.8 using a plain shapefile. pastebin.com/A4KTvqbY
    – Nathan W
    Commented May 28, 2013 at 6:43

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.