Reputation: 6965
I have array of Doubles:
var dates: [Double] = [1542412800000,
1542499200000,
1543017600000,
1543708800000,
1544659200000,
1547164800000,
1550880000000]
(yes, it's actually date timestamps). What I want is to transform it to an array of percentages. For example, if I had an array of [5, 20, 25], I want an output of [0.20, 0.8, 1], percentages of current values. I ended up with:
let percentages: [Double] = dates
.map{$0 - dates.first!}
.map{$0/dates.last!}
percentages.forEach{ it in
print(it)
}
But output is:
0.0
5.5710306406685235e-05
0.00038997214484679665
0.0008356545961002785
0.0014484679665738162
0.003064066852367688
0.005459610027855153
Second value kind of weird. How to solve this?
Upvotes: 0
Views: 38
Reputation: 27221
Using your code style you should use something like this:
var dates: [Double] = [1542412800000,
1542499200000,
1543017600000,
1543708800000,
1544659200000,
1547164800000,
1550880000000]
let intermediate: [Double] = dates
.map{$0 - dates.first!}
let percentages = intermediate
.map{$0/intermediate.last!}
percentages.forEach{ it in
print(it)
}
The real problem that you divide each element by 'initial' maximum value (not shifted by minimum value).
Upvotes: 1
Reputation: 18591
You can do it this way :
let dates: [Double] = [1542412800000,
1542499200000,
1543017600000,
1543708800000,
1544659200000,
1547164800000,
1550880000000]
let sorted = dates.sorted()
guard let first = sorted.first,
let last = sorted.last,
last != first
else {
fatalError()
}
let denominator = last - first
let percentages = sorted.map { ($0 - first)/denominator }
print(percentages) //[0.0, 0.01020408163265306, 0.07142857142857142, 0.15306122448979592, 0.2653061224489796, 0.5612244897959183, 1.0]
Upvotes: 2
Reputation: 12129
When dividing every value by the last value in the last map statement, you are ignoring the fact, that everything has already been subtracted by the first value.
To fix this, you should divide by the range of values (the difference between the maximum value and the minimum value).
Assuming your array is sorted, this will be:
guard let maxValue = dates.last, let minValue = dates.first else {
return
}
let percentages = dates
.map {$0 - minValue}
.map {$0 / (maxValue - minValue)}
This will normalize all values such that the first value is 0 and the last value is 1 and everything else is in between.
If you do not want to normalize the first value to 0 (but keep everything between 0 and 1), you can omit the subtraction step:
let percentages = dates.map {$0 / maxValue}
If your array is not sorted, you can use the .min()
and .max()
functions of your array:
guard let maxValue = dates.max(), let minValue = dates.min() else {
return
}
Upvotes: 2