Bring all values in an array closer together

Question:

I need to do is “crush” the values in l1 by some percentage so they are closer together such that perhaps if an array l1 were …

l1 =[10,20,30,40,50,60,70,80,90,100]

Then l2 could be…

l2 = [12.5, 25.0, 37.5, 50.0, 62.5, 45.0, 52.5, 60.0, 67.5, 75.0]

That can be done on a simple script such as…

for i in l1:
    if i <= 50:
        i = (i*1.25)
        l2.append(i)
        print(i)
    elif i >= 50:
        i = (i*0.75)
        l2.append(i)

print (l2)

So that’s to indicate I need to bring all the items closer together – ideally by some percentile (printed). The problem occurs when you have a list like this…

l1 =[4,2,3,4,3,6,4,8.6,10,7,12,4,14,15,26,14,15,16,10]

What I need to then do is bring all the items together discretely (so by some percentage) but in a loop. I need to “condense” or “crush” the values of the array, reduce the range between each number from smallest to biggest and biggest to smallest (closer to the median). I can’t just divide by the whole list since the ranges remain the same.

I thought one way to approach this (what I’m working on now) would be to (a) find the median of l1, (b) start from the smallest and biggest item in l1, increase that item by 10% of it’s value or decrease it by 10% of its value (in the case of the biggest item), then work to the second smallest and biggest items in the same loop (to avoid the script going over the same ‘smaller variable’ twice).

This would mean listing the values from biggest to smallest whilst maintaining their positions in the array (which are important), then searching through that list and making the changes for each corresponding value to the array l1.

For the attention of the proposed solution… One iteration of…

import statistics
a = [4, 3, 3, 4, 5, 1, 31, 321]
input_scope = 1.1

def scouter (input_list, scope):
    mean = statistics.mean(input_list)
    searchpositions = []
    for x, i in enumerate(input_list):
            print (x, i)
            if i == max(input_list) or i == min(input_list):
                searchpositions.append(x)

    for i in searchpositions:
        input_list[i] = [(input_list[i] - mean) / scope + mean]
    return (input_list)
print(scouter((a), input_scope))

Gives me what I need, sort of…

[4, 3, 3, 4, 5, [5.13636363636364], 31, [296.0454545454545]]

Output is lists in lists! Is there an easy way to eliminate this by re-writing the function?

Asked By: mmacheerpuppy

||

Answers:

Why not find the lowest number in your list and divide the whole list by that number? Or even any number that you want.

num = percentage/100 # gets you a decimal
l2 = [x*num for x in l1]

Use min() to find the lowest number of the list if you want to go that way.

Answered By: Sahil Agarwal

Just scale towards the median?

>>> l1 = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

>>> import statistics
>>> median = statistics.median(l1)
>>> [(x - median) / 10 + median for x in l1]
[50.5, 51.5, 52.5, 53.5, 54.5, 55.5, 56.5, 57.5, 58.5, 59.5]
Answered By: Stefan Pochmann

I know I’m late, but I know exactly what you need and I have the solution I used also for myself. I came up with this solution:

For every number n in the set, you get a new number x which is closer to the mean of the set, by this formula:

x = mean + factor * (n - mean)

Factor ranges from 0 to 1 and represents how much the numbers need to grow or shrink towards the average. 0 = all numbers become the average, 1 = all numbers remain the same. Tweak to your own needs.

The mean of the set is obviously the sum of every number in the set divided by the length of the set.

Hope this helps to whoever lands here in the future.

Answered By: ndrqu

Expanding on ndrqu’s answer:

import statistics as stats
l1_mean = stats.mean(l1)
factor = 0.99
l2 = []
for i in range(len(l1)):
    l2.append(l1_mean + factor * (l1[i] - l1_mean))

This will turn

l1 = [1,2,3,4,5]

into

l2 = [1.02,2.01,3.00,3.99,4.98]
Answered By: Willie Zenk
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.