2

I'm looking to spatial join points to their k nearest neighbors, with attributes summarized (mean, sum, sd, etc.).

I have looked at Join by Location (Summary) and Nearest Neighbor Analysis but the former doesn't allow nearness and the latter doesn't include a feature's attributes.

I imagine this is a duplicate but I've been unable to find anything.

3
  • What do you actually mean by "the former doesn't allow nearness and the latter doesn't include a feature's attributes"?
    – Taras
    Commented Apr 6, 2020 at 8:55
  • I'm guessing that OP means that Join by Location (Summary) can summarise values, but is based on spatial predicates (so, no knn algorithm), and Nearest Neighbor Analysis only works within a layer rather than between 2 layers.
    – she_weeds
    Commented Apr 6, 2020 at 10:09
  • @she_weeds interprets me well
    – Unrelated
    Commented Apr 6, 2020 at 14:04

1 Answer 1

2

The closest processing algorithm I can find is Join Attributes by Nearest which lets you select a maximum number of nearest neighbours and an optional maximum distance. You could select the relevant field(s) to join in this process. Now you'll have a row for each relation, and you can use any method to aggregate data based on the unique ID of your first layer using SQL in Virtual Layers, or QGIS expressions, or the Statistics by Categories processing algorithm.


Step 1: Join nearest features

Let's say, for example, your base layer is Layer1, with an unique id field. You want to join the field population from Layer2.

Open the Join Attributes by Nearest processing algorithm, and for 'Input layer', put in Layer1; for 'Input layer 2', put in Layer2. Select population under the 'Input Layer 2 fields to copy' section (click the three dots). Select whatever you want for maximum nearest neighbours and distance.

enter image description here

The resulting layer will have a row for each feature in Layer1 and its nearest n features from Layer2 (up to the max distance). Each row will have all the Layer1 fields (including id) and the population field from the nearest Layer2 feature along with some other details. There may be multiple instances of the same Layer1 feature if there were multiple Layer2 features matched. See the red circles in the image below.

enter image description here

Now, you want to group that data by the id field so that for each unique id value, you have an aggregate calculation of the population field from the nearest Layer2 features.


Step 2 option 1: Aggregate joined features (simple)

One simple method is the Statistics by Category processing tool. Under 'Input vector layer' select the joined layer from the previous step. 'Field to calculate statistics on' will be population from Layer2 (and make sure the data type is numeric). 'Field(s) with categories' will be your id field from Layer1.

enter image description here

The resulting table will return all the aggregate statistics you want - count, unique, min/max, sum, mean, std, etc. Notice the features where the aggregated values are from 2 or more original features from the previous screenshot.

enter image description here


Step 2 option 2: Aggregate joined features (advanced)

Another more flexible option is the Aggregate processing tool which lets you select as many fields as you want and what kind of aggregate function you want with each field.

In the example below in addition to a sum of population from Layer2, I also want to get an average and standard deviation for the cows and planes fields, and concatenate unique instances of the numeric ref identifying field with ; as a delimiter, all also from Layer2. While I'm at it I want to retain the number of features that matched (maximum of n) and the average distance from source and target feature(s) (average of distance), both fields generated from the Join Attributes by Nearest tool.

As above, aggregate by id but also ensure you keep that in your list - as it's the field we're grouping by just use first_value as the aggregate function. Use the buttons on teh right to add or remove fields and reorder them, and the dropdown boxes to select your aggregate function. You can rename output field names too. And don't forget to set your output field type, length and precision appropriately.

You can also use expressions to transform your source data - in this case ref was an integer field in Layer2 and couldn't be concatenated so I just used to_string() to change it to a string.

enter image description here

Result - notice it returns more results than the simpler tool because even if there was null/blank data in the matching Layer2 feature it still returns something. (Like id 17) enter image description here


Other options include SQL (SELECT id, max(population), max(someotherfield), max(someotherfield2), min(population),.... FROM joined_layer GROUP BY id) or a plugin like Dissolve with stats, or a Python algorithm.

If you need to do this for lots of layers consider automating the two-step process with the Model builder in the Processing toolbox.

2
  • Very helpful! I'm going to leave this open a bit longer but this solves my problem!
    – Unrelated
    Commented Apr 6, 2020 at 14:06
  • @Unrelated I have updated the answer with an alternative tool that will help you aggregate multiple fields at once. If this answers your problem do mark it as the accepted answer, it helps the site's stats :) (you can always change this later if another better answer comes along)
    – she_weeds
    Commented Apr 9, 2020 at 1:52

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.