14

This should be an easy one.

I have a list of numbers. How do I scale list's values, between -1.0 and 1.0 in order for min = -1 and max = 1.0?

Johnny
  • 183
  • 1
  • 1
  • 5
  • 1
    It depends. Do you want 0 to still equal 0 in the final result? – Alex Humphrey Aug 11 '10 at 17:06
  • 1
    @Alex: That may not even be possible if 0 is not in the range. – relet Aug 11 '10 at 17:09
  • 7
    What do you want to happen in the degenerate case where all the numbers are the same? – Hammerite Aug 11 '10 at 17:12
  • 1
    @relet - whether 0 is in the range or not doesn't matter - either we say the min number scales to -1 *and* the max number scales to 1, or we assume that we want to scale around 0, in which case we need only the number with the highest magnitude and scale that to either 1 or -1, and scale all the other numbers using the same ratio. I know what I mean :) – Alex Humphrey Aug 11 '10 at 17:13
  • I see that you know what you mean. :D – relet Aug 11 '10 at 17:51

3 Answers3

18

Find the min and the max

then for each number scale x to 2 * (x - min)/( max - min) - 1

Just to check --

  1. min scales to -1
  2. and max scales to 1

If it is a long list precomputing c = 2/(max - min) and scaling with c * x - 1 is a good idea.

mghie
  • 32,028
  • 6
  • 87
  • 129
deinst
  • 18,402
  • 3
  • 47
  • 45
  • I second the note about pre-computing where possible. Your compiler may not be smart enough to make that optimization automatically if you use the form above (it likely won't try to commute the `2` and the numerator to group the constants together). – bta Aug 11 '10 at 17:47
  • Just to annotate with an example (using the input I used in my answer for comparison), I believe the list [ -5, -3, -1, 0, 2, 4 ] would map to [ -1, -0.55555, -0.11111, 0.11111, 0.55555, 1 ]. This illustrates what I mean when I ask if we want 0 to still be equal to 0 in the results. – Alex Humphrey Aug 11 '10 at 20:31
  • @Alex Yes, but why would you expect 0 to map to 0? – deinst Aug 11 '10 at 21:30
  • I don't *expect* 0 to have to map to 0, it's just a possible case - the question doesn't state which is required. I didn't mean to criticize your answer in any way - it's certainly a perfectly good answer, and it looks like it might be just what is required. I just thought it was worth annotating it with a concrete example so that it's obvious how your and Mark's answers differ from mine. I guess I could have put it in my answer rather than cluttering your question's comments - I'll move it to my answer if you like. – Alex Humphrey Aug 11 '10 at 22:03
  • 1
    A generalization is; a = boundary_max - boundary_min; b = boundary_min; c = a / (max - min); finally for every x pass c * (x - min) + b. now for the required example set boundary_min = -1,boundary_max = 1. To get something akin to cumulative percentage set boundary_min = 0,boundary_max = 100. – LtPinback Aug 12 '10 at 17:20
5

This is a signed normalization

1 - get the Minimum and Maximum value on the list (MinVal,MaxVal)

2 - convert each number using this expressions signedNormal = (((originalNumber - Minimum) / (Maximum - Minimum)) * 2.0) - 1.0

I deliberately made this inefficient in order to be clear - more efficient would be

double min = myList.GetMinimum();
double max = myList.GetMaximum();
double signedRangeInverse = 1.0 / (max - min);
for(int i = 0;i < myList.NumberOfItems();i++)
  myList[i] = (((myList[i] - min) * signedRangeInverse) * 2.0) - 1

No point in recalculating range each time No point in dividing range, mult is faster

deinst
  • 18,402
  • 3
  • 47
  • 45
Mark Mullin
  • 1,340
  • 1
  • 9
  • 21
2

If you want 0 to still equal 0 in the final result:

  1. Find the number with the largest magnitude. This will either map to 1 or -1.
  2. Work out what you need to multiply it by to make it 1 or -1.
  3. Multiply all the numbers in the collection by that factor.

E.g

[ -5, -3, -1, 0, 2, 4]

Number with largest magnitude is -5. We can get that to equal -1 by multiplying by 0.2 (-1 / -5). (Beware of divide by 0s if your numbers are all 0s.)

So multiply all the elements by 0.2. This would give:

[-1, -0.6, -0.2, 0, 0.4, 0.8]

Although note that

[ -5, -5, -5 ] -> [ -1, -1, -1 ]

and

[ 5, 5, 5 ] -> [ 1, 1, 1 ]

and

[ 0, 0, 0 ] -> [ 0, 0, 0 ]

That may or may not be what you want. Thanks to @Hammerite for prompting me on that one with his very helpful comment :)

Alex Humphrey
  • 6,099
  • 4
  • 26
  • 41