5

I am keen to compute a windowing function on values in QGIS. I was not able to find a lag/lead function. I think this could be emulated using @rownumber and $currentfeature, but I am not expert in using these.

I need to find a value for an attribute for a current feature. This seems to be possible using attribute($currentfeature,"fieldname"). I am unsure how to specify something like the previous feature, equivalent to treating the list of values for an attribute in a feature collection (pseudocode):

fc[@rownumber]["fieldname"]

I could then do fc[@rownumber-offset]["fieldname"], assuming it evaluates to NULL for the first entry. Is there any way to do this? I am sure people computing say acceleration on trajecotry data would deal with this.

  • kudos: if the answer hints at treatment of the sorting in formats that do not support this well (shapefile).
  • be aware that rows inside a window do not necessarily mean features are spatial wise close to each other if this is what you are after – LaughU Jun 28 '17 at 5:51
  • 1
    No, I want a sort by an arbitrary attribute ( actually, time), and compute differences. Totally aspatial, but not keen to export the attributes, mess in Excel/R etc, and join back. That does not seem like a nice workflow. I would accept a solution in the Python console, but I am not familiar with the QGIS Python API yet. – MartinT Jun 28 '17 at 6:01
  • 1
    @MartinT - Perhaps you could edit your question to include a simple example? This could help potential answerers :) – Joseph Jun 28 '17 at 9:05
  • I put an example in the text, best way I could. In POSTGRES, this could be something similar to: SELECT event_id, date - lag(date) over (partition BY event_id ORDER BY date) as difference FROM TABLE – MartinT Jun 29 '17 at 4:30
3

If I understand well your request, you may create a simple custom function for accomplishing the task.

If you never used a custom function within the Field Calculator, please give a look at this post I recently wrote: How to create custom functions in QGIS using the Function Editor.

Having said that, you can use this custom function:

@qgsfunction(args='auto', group='Custom')
def my_calculation(my_layer, lag, lead, curr_row, feature, parent):

    layer=QgsMapLayerRegistry.instance().mapLayersByName(my_layer)[0]
    feature_count = layer.featureCount() # For evaluating the maximum number of 'rows'

    all_feats = {}
    row = 1
    for feat in layer.getFeatures():
        all_feats[row] = feat
        row += 1

    # Store all the features of interest in a list
    my_feats = [all_feats[x] for x in xrange(curr_row - lag, curr_row + lead + 1) if x > 0 and x <= feature_count]

    # Now, do what do you want with them
    # For example, let's compute the sum of the numbers stored in the field "pop":
    my_res = sum([k['pop'] for k in my_feats ]) # It is just an example, replace this with your calculations

    return my_res

You only need to replace the my_res variable with the calculation you want to do.

For activating the custom function, type an expression similar to this one:

my_calculation('my_layer', 2, 2, @row_number)

where the first parameter is the name of the layer you want to use, the second and the third ones are the lag and lead values and the last parameter specifies the current row number (leave it unchanged).

  • Ok, this is a step in the right direction, except - I would not like to return one single value as here (the sum of populations, say), but pairwise differences between the lead or lagged feature's value (so say my_feat[x]["popdif"]=my_feat[x]["pop"]-my_feat[x-1]["pop"]). How do you update the values for each actual current feature, then? – MartinT Jul 11 '17 at 5:37
  • @MartinT I think you should edit your original question by adding an example with real numbers because your comment is actually ambiguous and may involve different interpretations. A suggestion for your example: using a lag/lead value of 2 (as in my solution), and getting these 5 values for the current feature: [4, 16, 62, 27, 34], which would be the expected result? – mgri Jul 12 '17 at 13:31
  • Say you have lead=2, then diff current-lead(2) (equivalent for x(i)-x(i+2)), you get [4-62,16-27,62-34,None,None], and for lag it is similar, so current-lag(2) : [None, None, 62-4,27-16,34-62]. For completness, the function here is 'minus'. Hope this helps. – MartinT Aug 1 '17 at 13:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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