Replacing list of XYZ lists with one list where the Z entry are the averages of the Z value

I am trying to write a program to transform some XYZ coordinate data from a 3D scanner into a uniform grid. The goal is to be able to add or subtract future scans from the current scan. To do that, I group all coordinate sets that fall within a particular interval (x and y) and then average the value of z for that cell. Here is a synthetic example:

m = RandomInteger[{0, 10}, {50, 3}]

tablem = Table[
Select[
m, #[[2]] <= (i + 1)*1 && #[[2]] > i*1 && #[[1]] <= (j + 1)*1 && #[[1]] > j*1 &], {i, 0, 9}, {j, 0, 9}];


This gives me a 10x10 matrix containing all set of coordinates matching the coordinate rule.

My problem is that in some case, more than one coordinate set matches the rule (see example below). I want to replace these XYZ sets by one set, with the same x and y values but where the z value is the average of the z in all sets.

Any idea how to fix this? Also, any suggestions on other options to transform the XYZ data into a grid are welcome!

NB: Apologies for the poor image quality! This is my first post here.

Thank you for the help!

Rached

• Something like ReplacePart[tablem, {i_, j_} /; Length[tablem[[i,j]]] > 1 :> {{j, i, Mean[tablem[[i, j]][[All, 3]]]}}] should work. There are probably more succinct way to write it. Dec 5, 2016 at 7:53
• This works great! Thank you! Dec 5, 2016 at 8:03

Try this:

m = RandomInteger[{0, 10}, {50, 3}];


Here I am not sure, if I am allowed to remove some extra curly braces. If yes,

tablem = Flatten[
Table[Select[
m, #[[2]] <= (i + 1)*1 && #[[2]] >
i*1 && #[[1]] <= (j + 1)*1 && #[[1]] > j*1 &], {i, 0, 9}, {j,
0, 9}], 1]


Let us define a function doing the job:

Clear[lst];
f[lst_] :=
If[Length[lst] >= 2,
Transpose[
lst] /. {x_List, y_List, z_List} -> {x // First, y // First,
Mean[z]}, If[Length[lst] > 0, First[lst], lst]] // Quiet


Now

tablem


returns this:

(*
{{{1, 1, 1}}, {}, {}, {}, {}, {{6, 1, 3}}, {}, {{8, 1, 5}}, {{9, 1,
8}}, {}, {{1, 2, 8}}, {}, {}, {}, {{5, 2, 9}}, {}, {{7, 2,
5}}, {}, {{9, 2, 10}}, {}, {}, {}, {{3, 3, 3}}, {{4, 3,
6}}, {}, {}, {}, {{8, 3, 6}}, {}, {{10, 3, 5}}, {}, {{2, 4,
6}}, {}, {}, {{5, 4, 4}, {5, 4, 0}}, {}, {}, {}, {{9, 4,
8}}, {}, {}, {}, {{3, 5, 7}}, {}, {}, {{6, 5, 2}}, {}, {}, {{9, 5,
5}, {9, 5, 1}}, {}, {}, {{2, 6, 0}}, {{3, 6, 10}}, {{4, 6,
7}}, {}, {}, {}, {{8, 6, 0}, {8, 6, 1}}, {{9, 6,
0}}, {}, {}, {}, {{3, 7, 3}, {3, 7, 2}}, {}, {}, {}, {{7, 7,
8}, {7, 7, 1}}, {}, {}, {}, {}, {{2, 8, 5}}, {{3, 8,
7}}, {}, {}, {}, {{7, 8, 7}}, {}, {}, {}, {}, {}, {{3, 9, 2}, {3,
9, 0}}, {}, {}, {}, {{7, 9,
10}}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {{7, 10, 3}}, {{8, 10,
5}, {8, 10, 10}}, {}, {}}
*)


and

Map[f, tablem]


returns this:

(*
{{}, {2, 1, 10}, {3, 1, 7}, {4, 1, 10}, {}, {6, 1,
5}, {}, {}, {}, {}, {}, {}, {3, 2,
3}, {}, {}, {}, {}, {}, {}, {}, {1, 3, 1}, {2, 3, 9}, {3, 3,
9}, {}, {}, {}, {}, {}, {9, 3, 6}, {}, {}, {2, 4, 3}, {3, 4, 9/
2}, {}, {}, {6, 4, 5}, {}, {8, 4, 0}, {9, 4, 1}, {10, 4, 4}, {}, {2,
5, 5}, {3, 5, 0}, {}, {5, 5, 5}, {}, {}, {}, {}, {10, 5,
2}, {}, {}, {}, {}, {}, {}, {7, 6, 5}, {}, {}, {10, 6,
2}, {}, {}, {3, 7, 6}, {}, {}, {}, {}, {8, 7, 5}, {9, 7, 10}, {10,
7, 9/2}, {}, {2, 8, 3}, {3, 8, 9/2}, {}, {}, {6, 8, 4}, {}, {}, {9,
8, 4}, {10, 8, 7}, {}, {}, {}, {4, 9, 10}, {}, {6, 9,
2}, {}, {}, {}, {10, 9, 8}, {}, {}, {}, {}, {}, {}, {}, {}, {9, 10,
3}, {10, 10, 5}}
*)


Hope it helps, have fun!

If you just want to add the values, then this gives you a matrix of the z-values:

mat = Mean /@ GroupBy[m, Most -> Last] // KeyMap[1 + # &] // Normal // SparseArray;
mat // MatrixForm


Perhaps

SeedRandom[1]
m = RandomInteger[{0, 5}, {20, 3}]


{{4, 2, 4}, {0, 1, 0}, {0, 2, 0}, {0, 3, 5}, {2, 0, 3}, {4, 4, 1}, {3, 3, 4}, {1, 4, 2}, {1, 1, 4}, {5, 4, 5}, {0, 3, 3}, {0, 0, 2}, {3, 1, 1}, {3, 2, 5}, {1, 1, 4}, {0, 1, 1}, {3, 5, 5}, {2, 3, 0}, {0, 2, 5}, {4, 5, 2}}

Join[#[[1, ;; 2]], {Mean[#[[All, 3]]]}] & /@ GatherBy[m , #[[;; 2]] &]


{{4, 2, 4}, {0, 1, 1/2}, {0, 2, 5/2}, {0, 3, 4}, {2, 0, 3}, {4, 4, 1},
{3, 3, 4}, {1, 4, 2}, {1, 1, 4}, {5, 4, 5}, {0, 0, 2}, {3, 1, 1},
{3, 2, 5}, {3, 5, 5}, {2, 3, 0}, {4, 5, 2}}

In version 10 and 11, you can use Merge:

Append @@@ Normal @ Merge[Mean][{#, #2} -> #3& @@@ m]


{{4, 2, 4}, {0, 1, 1/2}, {0, 2, 5/2}, {0, 3, 4}, {2, 0, 3}, {4, 4, 1},
{3, 3, 4}, {1, 4, 2}, {1, 1, 4}, {5, 4, 5}, {0, 0, 2}, {3, 1, 1},
{3, 2, 5}, {3, 5, 5}, {2, 3, 0}, {4, 5, 2}}