views:

164

answers:

3

This should be an easy one:

I have a list of numbers. How do I scale them to be between -1.0 and 1.0 such that the smallest value is -1.0 and the largest is 1.0?

+4  A: 

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.

deinst
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
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
@Alex Yes, but why would you expect 0 to map to 0?
deinst
@deinst: 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
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
+2  A: 

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

Mark Mullin
+1  A: 

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