8

I have a list of decimal numbers as follows:

[-23.5, -12.7, -20.6, -11.3, -9.2, -4.5, 2, 8, 11, 15, 17, 21]

I need to normalize this list to fit into the range [-5,5].
How can I do it in python?

Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
Surjya Narayana Padhi
  • 7,741
  • 25
  • 81
  • 130

5 Answers5

14

To get the range of input is very easy:

old_min = min(input)
old_range = max(input) - old_min

Here's the tricky part. You can multiply by the new range and divide by the old range, but that almost guarantees that the top bucket will only get one value in it. You need to expand your output range so that the top bucket is the same size as all the other buckets.

new_min = -5
new_range = 5 + 0.9999999999 - new_min
output = [floor((n - old_min) / old_range * new_range + new_min) for n in input]
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Why do you use a "magic number" `0.9999999999`? What if someone have a range [0,1]? What will be that number then? – Nairum Apr 19 '21 at 16:00
  • @Nairum as I explain in the answer, the "magic number" is always the same; the intent is to be just a hair under 1. Because the outputs are integers, any fractional part will be lost. For the range [0,1] you don't want to scale exactly to the range [0,1.0], or almost all your inputs will be converted to 0. If you scale to [0,1.9999999999] instead you'll get a much more even distribution. – Mark Ransom Apr 19 '21 at 18:03
  • @Nairum thanks for making me look at this answer again, you made me realize there was a bug; the output range (-1,1) mapped to 0, making that bucket twice as big as all the others. – Mark Ransom Apr 19 '21 at 18:09
4
>>> L = [-23.5, -12.7, -20.6, -11.3, -9.2, -4.5, 2, 8, 11, 15, 17, 21]
>>> normal = map(lambda x, r=float(L[-1] - L[0]): ((x - L[0]) / r)*10 - 5, L)
>>> normal
[-5.0, -2.5730337078651684, -4.348314606741574, -2.2584269662921352, -1.7865168539325844, -0.7303370786516856, 0.7303370786516847, 2.0786516853932575, 2.752808988764045, 3.6516853932584272, 4.101123595505618, 5.0]
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
1
original_vals = [-23.5, -12.7, -20.6, -11.3, -9.2, -4.5, 2, 8, 11, 15, 17, 21 ]

# get max absolute value
original_max = max([abs(val) for val in original_vals])

# normalize to desired range size
new_range_val = 5
normalized_vals = [float(val)/original_max * new_range_val for val in original_vals]
mdscruggs
  • 1,182
  • 7
  • 15
  • 2
    Just a minor point, you don't need the inner list in `max`: `max(abs(val) for val in original_vals)` will also work. – Burhan Khalid May 13 '13 at 04:08
  • 1
    Thanks- always good to use generators! I also just realized the last line is vulnerable to zero division, which is an advantage for the range-based approach. – mdscruggs May 13 '13 at 04:21
0

Assuming your list is sorted:

# Rough code
# Get the range of the list
r = float(l[-1] - l[0])
# Normalize
normal = map(lambda x: (x - l[0]) / r, l)

Basically, you want to adjust the base of the list into a different range. This will normalize your original list into [0, 1]

Michael
  • 2,181
  • 14
  • 14
-4

Keep it simple:

>>> foo = [-23.5, -12.7, -20.6, -11.3, -9.2, -4.5, 2, 8, 11, 15, 17, 21]
>>> [i for i in foo if int(i) in range(-5, 5)]
[-4.5, 2]

Additionally, if you want the result to just be integers:

>>> [int(i) for i in foo if int(i) in range(-5, 5)]
[-4, 2]
timss
  • 9,982
  • 4
  • 34
  • 56
  • @Cerin Not denying that's probably the case, this question is over a year old and my response was before there was any accepted answer or feedback from the OP. No need to revive this. – timss Jul 08 '14 at 09:57