6
$\begingroup$

I am trying to plot a list of tuples where each “x” coordinate has multiple corresponding “y” values, sort of like {{1, 0.5}, {1, 0.7}, {2, 0.8}, {2, 1.0}…}. I want to create a new list where all the “y” coordinates corresponding to a particular “x” coordinate have been averaged over. So, the previous list would be transformed into {{1, 0.6}, {2, 0.9}…}.

What is the cleanest way I can achieve this? I don’t want to write a for-loop or something like that for such a seemingly-trivial thing.

$\endgroup$
1
  • 5
    $\begingroup$ If your list isn't already sorted then SortBy[yourlist,First] will sort by x values. Then SplitBy[thatresult,First] will break those into groups of equal x values. Then Map[Mean,thatnewresult] will give you a list of {x,yavg} for each of your x. Try that, step by step, and see the result. Or ListPlot[Map[Mean,SplitBy[SortBy[yourlist,First],First]]] Study that until you think you understand my thinking. $\endgroup$
    – Bill
    Jul 29 at 2:33

3 Answers 3

7
$\begingroup$

TDIL that TemporalData[data] does the desired grouping/sorting/averaging when input data contains repeated time stamps.

data = {{1, a}, {1, b}, {5, w}, {2, u}, {5, x}, {5, y}, {5, z}};

TemporalData[data]["Path"]
 {{1, (a + b)/2}, {2, u}, {5, 1/4 (w + x + y + z)}}

Using example data from Bob Hanlon's answer:

SeedRandom[1234];
n = 20;
data = Sort @ Transpose[{RandomInteger[5, n], RandomReal[1, n]}];

simply use {data, TemporalData[data]} in the first argument of ListPlot:

ListPlot[{data, TemporalData[data]}, 
 Joined -> {False, True}, 
 PlotTheme -> "OpenMarkersThick", 
 PlotLegends -> {"data", "means"}]

enter image description here

means = TemporalData[data]["Path"]
{{0, 0.62577}, {1, 0.856999}, {2, 0.684749}, {3, 0.329375},   
 {4, 0.255957}, {5, 0.414358}}

This matches the data set obtained using methods suggested in Bob Hanlon's and upbdqn's answers and Bill's comment:

TemporalData[data]["Path"] ==
 Map[Mean] @ GatherBy[data, First] ==
 Values @ GroupBy[data, First, Mean] ==
 Map[Mean] @ SplitBy[Sort@data, First] ==
 KeyValueMap[List] @ Merge[Mean] @ MapApply[Rule] @ data
True
$\endgroup$
7
$\begingroup$

You can also use GroupBy:

Using the same toy data:

SeedRandom[1234];
n = 20;
data = Sort@Transpose[{RandomInteger[5, n], RandomReal[1, n]}];

Using GroupBy:

gb = GroupBy[data, First, {#[[1, 1]], Mean@#[[All, 2]]} &];
ListPlot[{data, Values[gb]}, Joined -> {False, True}, 
 PlotTheme -> "OpenMarkersThick"]

enter image description here

EDIT

Better answer as per @kglr.

See comment by @kglr (as the larst argument applies to grouped data):

gb = GroupBy[data, First, Mean];
ListPlot[{data, Values[gb]}, Joined -> {False, True}, 
 PlotTheme -> "OpenMarkersThick"]
$\endgroup$
2
  • $\begingroup$ you could also use GroupBy[data, First, Mean] $\endgroup$
    – kglr
    Jul 29 at 5:05
  • $\begingroup$ @kglr of course, much better. $\endgroup$
    – ubpdqn
    Jul 29 at 6:40
6
$\begingroup$
$Version

(* "13.3.0 for Mac OS X ARM (64-bit) (June 3, 2023)" *)

Clear["Global`*"]

SeedRandom[1234];

n = 20;

data = Sort@Transpose[
   {RandomInteger[5, n], RandomReal[1, n]}]

(* {{0, 0.113153}, {0, 0.233079}, {0, 0.42294}, {0, 0.875912}, {0, 
  0.87966}, {0, 0.884627}, {0, 0.971018}, {1, 0.754358}, {1, 
  0.95964}, {2, 0.397409}, {2, 0.544858}, {2, 0.87433}, {2, 
  0.922399}, {3, 0.329375}, {4, 0.0542848}, {4, 0.254132}, {4, 
  0.459454}, {5, 0.0637313}, {5, 0.331812}, {5, 0.847531}} *)

{#[[1, 1]], Mean[#[[All, 2]]]} & /@ GatherBy[data, First]

(* {{0, 0.62577}, {1, 0.856999}, {2, 0.684749}, {3, 0.329375}, {4, 
  0.255957}, {5, 0.414358}} *)

ListPlot[{data, data2},
 Joined -> {False, True},
 PlotMarkers -> Automatic]

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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