# Extracting certain elements from a ragged array

For the following let me use this notation:

$$\qquad \text{data}=\{\{x_1,y_{11}\}, \{x_1,y_{12}\},\{x_1,y_{1N}\},\,\dots,\,\{x_2,y_{21}\}, \{x_2,y_{22}\},\{x_2,x_{2M}\},\,\dots\}$$,

where $$M\neq N$$. What I'm trying to find are the smallest values, $$\{\{x_1,y_{11}\}, \{x_2,y_{21}\},\dots\}$$, and the largest values, $$\{\{x_1,y_{1N}\}, \{x_2,y_{2M}\},\,\dots\}$$

As an example, expressed in the Wolfram Language:

data =
{{0.2,0.255556}, {0.2,0.411111}, {0.2,0.566667}, {0.2,0.722222}, {0.2,0.877778}, {0.2,1.03333}, {0.2,1.18889}, {0.2,1.34444}, {0.2,1.5},
{0.4,0.411111}, {0.4,0.566667}, {0.4,0.722222}, {0.4,0.877778}, {0.4,1.03333}, {0.4,1.18889}, {0.4,1.34444}, {0.4,1.5},
{0.6,0.566667}, {0.6,0.722222}, {0.6,0.877778}, {0.6,1.03333}, {0.6,1.18889}, {0.6,1.34444}, {0.6,1.5},
{0.8,0.722222}, {0.8,0.877778}, {0.8,1.03333}, {0.8,1.18889}, {0.8,1.34444}, {0.8,1.5},
{1.,1.03333}, {1.,1.18889}, {1.,1.34444}, {1.,1.5},{1.2,1.18889}, {1.2,1.34444}, {1.2,1.5}, {1.4,1.5}}

SmallestValues =
{{0.2, 0.255556}, {0.4, 0.411111}, {0.6, 0.566667}, {0.8, 0.722222}, {1., 1.03333}, {1.2, 1.18889}, {1.4, 1.5}}
BiggestValues =
{{0.2, 1.5}, {0.4, 1.5}, {0.6, 1.5}, {0.8, 1.5}, {1., 1.5}, {1.2, 1.5}, {1.4, 1.5}}


My problem is that I honestly can't think of a way to get this done in Mathematica. I'm used to python coding and there I would probably change the type from a list to something like a matrix and then manipulate this using for-loops, but this seems rather inefficient and is presumably not necessary here.

• Do you mean "from" instead of "form"? – user64494 May 3 '19 at 18:52

Join @@ Values @ GroupBy[data, First, MinimalBy[Last]]


{{0.2, 0.255556}, {0.4, 0.411111}, {0.6, 0.566667}, {0.8,   0.722222}, {1., 1.03333}, {1.2, 1.18889}, {1.4, 1.5}}

Join @@ Values @ GroupBy[data, First, MaximalBy[Last]]


{{0.2, 1.5}, {0.4, 1.5}, {0.6, 1.5}, {0.8, 1.5}, {1., 1.5}, {1.2,   1.5}, {1.4, 1.5}}

If your data is guaranteed to be ordered consecutively for the same $$x$$, then:

groups = SplitBy[data, #[[1]] &];
SmallestValues = groups[[All, 1]];
BiggestValues = groups[[All, -1]];


If not, you can look into GroupBy function and the code will be similar.

You may compose a few functions together to collect your results. There are a few ways to compose the functions.

With Query and RightComposition

{smallest, biggest} =
Query[GroupBy[First -> Last] /* KeyValueMap[Thread[{##}] &] /* Transpose, MinMax]@data


With Map and Prefix.

{smallest, biggest} =
Transpose@KeyValueMap[Thread[{##}] &, MinMax /@ GroupBy[First -> Last]@data]


Both give

{
{{0.2,0.255556},{0.4,0.411111},{0.6,0.566667},{0.8,0.722222},{1.,1.03333},{1.2,1.18889},{1.4,1.5}},
{{0.2,1.5},{0.4,1.5},{0.6,1.5},{0.8,1.5},{1.,1.5},{1.2,1.5},{1.4,1.5}}
}


Hope this helps.

KeyValueMap[List]@GroupBy[data, First -> Last, Min]


{{0.2, 0.255556}, {0.4, 0.411111}, {0.6, 0.566667}, {0.8, 0.722222}, {1., 1.03333}, {1.2, 1.18889}, {1.4, 1.5}}

KeyValueMap[List]@GroupBy[data, First -> Last, Max]


{{0.2, 1.5}, {0.4, 1.5}, {0.6, 1.5}, {0.8, 1.5}, {1., 1.5}, {1.2, 1.5}, {1.4, 1.5}}

Both at once:

KeyValueMap[List]@GroupBy[data, First -> Last, MinMax]


{{0.2, {0.255556, 1.5}}, {0.4, {0.411111, 1.5}}, {0.6, {0.566667, 1.5}}, {0.8, {0.722222, 1.5}}, {1., {1.03333, 1.5}}, {1.2, {1.18889, 1.5}}, {1.4, {1.5, 1.5}}}

You can leave out the KeyValueMap[List]@ part and only use the GroupBy part if you want to have an association instead of a list.

This works even if the data are out of order:

g = Gather[Sort[data], #1[[1]] == #2[[1]] &];

Map[Sort, g][[All, 1]]

{{0.2, 0.255556}, {0.4, 0.411111}, {0.6, 0.566667}, {0.8, 0.722222}, {1., 1.03333}, {1.2, 1.18889}, {1.4, 1.5}}

Map[Sort, g][[All, -1]]

{{0.2, 1.5}, {0.4, 1.5}, {0.6, 1.5}, {0.8, 1.5}, {1., 1.5}, {1.2, 1.5}, {1.4, 1.5}}