Weighted average - the pressup trend problem

Let's say that this array is how many press-ups I've achieved each day in the last 28 days:

[
20,20,20,30,30,30,30,
35,35,40,40,40,45,45,
50,50,50,50,50,50,50,
60,70,80,90,100,110,120
]

As you can see, it's taken a steep upward trend in the last week, and that's the part of this data I'm most interested in. The further in the past it is, the less I want that data to feature in my 'average' number of press-ups.

To that end, I want to work out an 'average' where each week is worth more than the previous week.

Background information, not part of this problem.

Normal average:

The sum of all values / the number of values

For above:

1440 / 28 = 51.42857142857143

Weighted average:

Split the array into 4 groups of 7, and start up a new array.

• Add the first group to the array.
• Add the second group to the array twice.
• Add the third group to the array thrice.
• Add the fourth group to the array four times.

Sum all of the new array, and divide by the length of the new array.

For above:

Convert the array to this:

[
20,20,20,30,30,30,30, # first week once
35,35,40,40,40,45,45,
35,35,40,40,40,45,45, # second week twice
50,50,50,50,50,50,50,
50,50,50,50,50,50,50,
50,50,50,50,50,50,50, # third week thrice
60,70,80,90,100,110,120,
60,70,80,90,100,110,120,
60,70,80,90,100,110,120,
60,70,80,90,100,110,120 # Fourth week four times
]

Then run a normal average on that array.

4310 / 70 = 61.57142857142857

Note that it's higher than the normal average value because of the upward trend in the last week.

The rules:

• The input is a flat array of 28 nonnegative integers.
• Any language you'd like to write in.
• Output a number.
• I always like to see TIO links.
• Try to solve the problem in the smallest number of bytes.
• The result should be a decimal accurate to at least 4 decimal places (either truncated or rounded up from the test case values is fine) or an exact fraction.

Test cases:

Case 1: Upward trend

[
20,20,20,30,30,30,30,
35,35,40,40,40,45,45,
50,50,50,50,50,50,50,
60,70,80,90,100,110,120
]

Normal average: 51.42857142857143 Weighted average: 61.57142857142857

Case 2: Leaving the lull behind

[
50,50,50,50,50,50,50,
10,10,10,10,10,10,10,
50,50,50,50,50,50,50,
50,50,50,50,50,50,50
]

Normal average: 40 Weighted average: 42

Case 3: Giving up

[
50,50,50,50,50,50,50,
50,50,50,50,50,50,50,
50,50,50,50,50,50,50,
10,10,10,10,10,10,10
]

Normal average: 40 Weighted average: 34

Case 4: Averaging out

Okay, so I'm just playing around here, I thought it might be the same value for the normal and weighted averages, but, of course, it was not.

[
60,60,60,60,60,60,60,
30,30,30,30,30,30,30,
20,20,20,20,20,20,20,
15,15,15,15,15,15,15
]

Normal average: 31.25 Weighted average: 24.0

Bonus problem:

What combination of 28 values would have the same normal average and weighted average?

Happy golfing!

• May 2 '18 at 16:55
• You might want to try exponential smoothing too - new_avg = α*weekly_sum + (1-α)*old_avg for some α∈(0,1)
– Angs
May 2 '18 at 17:16
• I do 0 press-ups every day, so my weighted average is the same as my normal average.
– Neil
May 2 '18 at 20:40
• @Neil you would not benefit from the weighted average system ;) May 2 '18 at 20:41
• be careful not to overtrain :p May 3 '18 at 8:36

Python 3, 38 bytes

lambda x:sum(x+x[7:]+x[14:]+x[21:])/70

Try it online!

05AB1E, 8 7 bytes

Saved 1 byte thanks to Mr. Xcoder

7ô.s˜ÅA

Try it online!

Explanation

7ô         # split list into groups of 7
.s       # push suffixes
˜      # flatten
ÅA    # arithmetic mean
• @Mr.Xcoder: Oh yeah, I knew I had seen a mean function, but I couldn't find it :P May 2 '18 at 17:06

Jelly, 7 bytes

s7ṫJFÆm

Try it online!

How it works

s7ṫJFÆm  Main link. Argument: A (array of length 28)

s7       Split the array into chunks of length 7.
J     Indices; yield [1, ..., 28].
ṫ      Tail; yield the 1st, ..., 28th suffix of the result to the left.
Starting with the 5th, the suffixes are empty arrays.
F    Flatten the resulting 2D array.
Æm  Take the arithmetic mean.
• Huh, so x"J$is equivalent to ṫJ in this context. Interesting! May 2 '18 at 16:58 • Sort of. Instead of repeating the elements of the n-th array n times, this takes all suffixes. After flattening, it generates the same elements, but in a different order. May 2 '18 at 17:00 R+pryr, 32 28 bytes and the same average score week over week would result in equality of the means. pryr::f(s%*%rep(1:4,e=7)/70) Try it online! Saved 4 bytes by using dot product thanks to Giuseppe. Pure R would have two more bytes using function • Of course it would, that's so obvious, now I think about it. May 2 '18 at 16:54 • 28 bytes using dot product instead of sum May 2 '18 at 16:58 • I had 40 bytes with function(s)weighted.mean(s,rep(1:4,e=7)) May 2 '18 at 16:58 • @Giuseppe Luckily I did not remember weighted.mean. Love it when R outgolfs Python. May 2 '18 at 17:27 MATL, 10 bytes 7es4:*s70/ Try it online! I haven't posted a MATL answer in ages! Figured I might participate as part of LOTM May 2018! Explanation: 7e % Reshape the array into 7 rows (each week is one column) s % Sum each column 4: % Push [1 2 3 4] * % Multiply each columnar sum by the corresponding element in [1 2 3 4] s % Sum this array 70/ % Divide by 70 • I had K:7Y"*s70/ at 10 bytes as well. May 4 '18 at 22:20 Husk, 6 bytes AΣΣṫC7 Try it online! Uses the trick Dennis used to outgolf my Jelly submission. Instead of repeating each chunk N times, it retrieves the suffixes of the list of chunks, which after flattening will yield the same result, except for the order. Jelly, 9 bytes s7x"J$FÆm

Try it online!

How it works

s7x"J$FÆm – Takes input from the first command line argument and outputs to STDOUT. s7 – Split into groups of 7. " – Apply vectorised (zipwith): x J$    – Repeat the elements of each list a number of times equal to the list's index.
F   – Flatten.
Æm – Arithmetic mean.

(/70).sum.zipWith(*)([1..]<*[1..7])

Bonus: if a,b,c,d are the weekly sums, normal average is the same as weighted average iff:

(a + b + c + d)/4 = (a + 2b + 3c + 4d)/10  <=>
10(a + b + c + d) = 4(a + 2b + 3c + 4d)    <=>
5(a + b + c + d)  = 2(a + 2b + 3c + 4d)    <=>
5a + 5b + 5c + 5d = 2a + 4b + 6c + 8d      <=>
3a + b - c - 3d   = 0

One solution is when first and last week have same sums, and likewise second and third weeks have the same sum, but there are infinitely many solutions if your biceps are up to it. Example: [15,10,10,10,10,10,5,20,20,20,25,25,20,20,30,20,20,20,20,20,20,10,10,20,0,10,10,10]

Try it online!

JavaScript (Node.js), 49 bytes

a=>a.map((x,i)=>(I+=d=-~(i/7),s+=x*d),s=I=0)&&s/I

Try it online!

Non generics solution

JavaScript (Node.js), 39 36 bytes

a=>a.reduce((s,x,i)=>s+x*-~(i/7))/70

Try it online!

• -1 byte on the first solution using a=>a.reduce((s,x,i)=>(I+=d=-~(i/7),s+x*d),I=0)/I. And a quick tip: use <hr> to create a horizontal line in markdown May 2 '18 at 17:06
• @HermanL What's wrong with using --- (needs its own paragraph)?
– Neil
May 2 '18 at 20:30

Stax, 6 bytes

ñI"%"╟

Run and debug it at staxlang.xyz!

Unpacked (7 bytes) and explanation:

7/|]$:V 7/ Split into groups of seven. |] Suffixes$:V    Flatten and average. Implicit print as fraction.

Stax, 10 8 bytes

äΔ6◙█µøΓ

Run and debug it

Explanation (unpacked):

7/4R:B$:V Full program, implicit input 7/ Split into parts of length 7 4R Push [1, 2, 3, 4] :B Repeat each element the corresponding number of times$   Flatten
:V Average
• Another using Stax! Yes! You can use $to flatten if the elements are all integers—checking with OP now. May 2 '18 at 17:33 Octave, 33 bytes @(x)sum(reshape(x,7,4)*(1:4)')/70 Try it online! Charcoal, 14 bytes Ｉ∕ΣＥ⪪Ａ⁷×Σι⊕κ⁷⁰ Try it online! Link is to verbose version of code. Explanation: Ａ Input array ⪪ ⁷ Split into subarrays of length 7 Ｅ Loop over each subarray ι Subarray Σ Sum κ Loop index ⊕ Incremented × Product Σ Sum results ⁷⁰ Literal 70 ∕ Divide Ｉ Cast to string Implicitly print K4 / K (oK), 1916 14 bytes Solution: +/(1+&4#7)%70% Try it online! Example: +/(1+&4#7)%70%50 50 50 50 50 50 50 10 10 10 10 10 10 10 50 50 50 50 50 50 50 50 50 50 50 50 50 50 42 Explanation: Evaluation is perform right-to-left. Divide 7 1s, 7 2s, 7 3s and 7 4s by 70 divided by the input; then sum up. +/(1+&4#7)%70% / the solution 70% / 70 divided by the input ( )% / the stuff in brackets divided by this... 4#7 / draw from 7, 4 times => 7 7 7 7 & / 'where' builds 7 0s, 7 1s, 7 2s, 7 3s 1+ / add one +/ / sum (+) over (/) to get the total Excel: 33 bytes (3 bytes saved from @wernisch's answer by running data on 2 lines from A1:N1 and A2:N2) =AVERAGE(A1:N2,H1:N2,A2:N2,H2:N2) Apologies for not including this as a comment. I don't have enough reputation to do so. Japt, 11 10 bytes xÈ/#F*ÒYz7 Try it Explanation È :Pass each element at index Y through a function /#F : Divide by 70 Yz7 : Floor divide Y by 7 Ò : Negate the bitwise NOT of that to add 1 * : Multiply both results x :Reduce by addition Triangularity, 49 bytes ....).... ...D7)... ..14)21.. .WM)IEtu. }u)70s/.. Try it online! Explanation )D7)14)21WM)IEtu}u)70s/ – Full program. )D7)14)21 – Push the literals 0, 7, 14, 21 onto the stack. WM } – Wrap the stack to a list and run each element on a separate stack, collecting the results in a list. )IEt – Crop the elements of the input before those indices. u – Sum that list. u – Then sum the list of sums. )70 – Push the literal 70 onto the stack. s/ – Swap and divide. Perl 5-pa, 28 bytes$\+=$_/70*int$i++/7+1for@F}{

Try it online!

Input is space separated rather than comma separated.

• You have $. available as the perfect multiplier. No need for$i May 3 '18 at 7:34

APL+WIN, 13 bytes

Prompts for array as a vector of integers:

(+/⎕×7/⍳4)÷70

Explanation:

7/⍳4) create a vector comprising 7 1s, 7 2s, 7 3s and 7 4s

+/⎕× prompt for input, multiply by the vector above and sum result

(....)÷70 divide the above sum by 70

Java 8, 57 bytes

a->{int r=0,i=35;for(;i-->7;)r+=i/7*a[i-7];return r/70d;}

Try it online.

Explanation:

a->{              // Method with integer-array parameter and double return-type
int r=0,        //  Result-sum, starting at 0
i=35;       //  Index-integer, starting at 35
for(;i-->7;)    //  Loop i downwards in the range (35,7]
r+=           //   Add the following to the result-sum:
i/7        //    i integer-divided by 7,
*a[i-7];   //    multiplied by the item at index i-7
return r/70d;}  //  Return the result-sum, divided by 70.0

J, 16 bytes

70%~1#.]*7#4{.#\

Explanation:

#\           finds the lengths of all successive prefixes (1 2 3 4 ... 28)
4{.             takes the first 4 items (1 2 3 4)
7#                creates 7 copies of each element of the above list
]*                  multiplies the input by the above
1#.                    sum
70%~                       divide by 70

Try it online!

Clojure, 48 46 bytes

#(/(apply +(for[i[0 7 14 21]v(drop i %)]v))70)

This ended up being shorter than mapcat + subvec combination.

TI-Basic, 25 bytes

mean(Ansseq(sum(I>{0,7,21,42}),I,1,70

Alternate solution, 39 bytes

Input L1
For(I,1,70
Ans+L1(I)sum(I>{0,7,21,42
End
Ans/70

Ruby, 65 bytes

->r{(b=(0..r.size/7).map{|a|r[a*7..-1]}.flatten).sum/b.size.to_f}

Try it online!

• The input size is specified to be fixed at 28 here - so you can save several bytes by hardcoding the values instead of using the size property. Try it online! Jul 2 '18 at 14:37

Excel, 36 33 bytes

-3 bytes thanks to @tsh.

=SUM(1:1,H1:AB1,O1:AB1,V1:AB1)/70

Input in first row (A1 to AB1).

• Maybe A1:AB1 -> 1:1?
– tsh
May 8 '18 at 5:27

Julia 0.6, 27 bytes

p->repeat(1:4,inner=7)'p/70

Try it online!

The repeat call forms a column matrix of 28 values, containing seven 1's, then seven 2's, etc. We then transpose it with ', then do a matrix multiplication with the input (mutiplication is implicit here). Since it's a matrix multiplication of a 1x28 matrix with a 28x1 matrix, we end up with a single value, which is the weighted sum we need. Divide that by 70 to get our weighted mean.