Rails average - collect only if value <> 0 - ruby-on-rails

I'm using this code to calculate the average of some numbers - note that the rankings column is serialized and contains a hash:
#reviews.collect{|review| review.rankings[label].to_i}.sum.to_f/#reviews.length if #reviews.length > 0
The drawback with this code is this: if one of the values is nil, the average calculates as if it existed as a zero. The problem is basically that the #reviews.length denominator assumes every review has a relevant value.
How do I exclude from the denominator those reviews that don't count?
Thank you very much for your answers. The problem ended up being with the denominator, i.e. the /#reviews.length part. When I used reject on both numerator and denominator, the equation stopped dividing by total length and started dividing by (length - no of nils).
End result - 2 scores [0 and 100] average to 50; [nil and 100] average to 100.

Array#reject to the rescue.
#reviews.reject{|review| review.rankings[label].nil?}.collect{|review| review.rankings[label].to_i}.sum.to_f/#reviews.length if #reviews.length > 0

I would go with that approach (using compact to kill the nils):
rankings = #reviews.map{ |review| review.rankings[label] }.compact
rankings.map(&:to_i).sum.to_f / rankings.count unless rankings.count == 0

if #reviews.length > 0
#reviews.collect{|r| r.rankings[label]}.compact.map(&:to_f).sum / #reviews.length
else
0
end

Related

Alternative to Modulus in Lua

In Lua, as you may know, arrays start with index 1.
In other languages I will often use modulus to make a value oscillate through the members of an array, for example:
i = (i + 1) % array.length
return array[1]
How can I do this in Lua, where array[0] is nil by default.
Like Egor said in the comments,
First do the modulo and then increment the value.
If i is equal to the length it will end up 0. Incrementing that will result in 1. Every other value just gets incremented.
This only works when incrementing by 1 though. For bigger steps you can do
i = (i+n-1)% #array + 1

Lua: Rounding numbers and then truncate

Which is the best efficient way to round up a number and then truncate it (remove decimal places after rounding up)?
for example if decimal is above 0.5 (that is, 0.6, 0.7, and so on), I want to round up and then truncate (case 1). Otherwise, I would like to truncate (case 2)
for example:
232.98266601563 => after rounding and truncate = 233 (case 1)
232.49445450000 => after rounding and truncate = 232 (case 2)
232.50000000000 => after rounding and truncate = 232 (case 2)
There is no build-in math.round() function in Lua, but you can do the following:
print(math.floor(a+0.5)).
A trick that is useful for rounding at decimal digits other than whole integers is to pass the value through formatted ASCII text, and use the %f format string to specify the rounding desired. For example
mils = tonumber(string.format("%.3f", exact))
will round the arbitrary value in exact to a multiple of 0.001.
A similar result can be had with scaling before and after using one of math.floor() or math.ceil(), but getting the details right according to your expectations surrounding the treatment of edge cases can be tricky. Not that this isn't an issue with string.format(), but a lot of work has gone into making it produce "expected" results.
Rounding to a multiple of something other than a power of ten will still require scaling, and still has all the tricky edge cases. One approach that is simple to express and has stable behavior is to write
function round(exact, quantum)
local quant,frac = math.modf(exact/quantum)
return quantum * (quant + (frac > 0.5 and 1 or 0))
end
and tweak the exact condition on frac (and possibly the sign of exact) to get the edge cases you wanted.
To also support negative numbers, use this:
function round(x)
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5)
end
If your Lua uses double precision IEC-559 (aka IEEE-754) floats, as most do, and your numbers are relatively small (the method is guaranteed to work for inputs between -251 and 251), the following efficient code will perform rounding using your FPU's current rounding mode, which is usually round to nearest, ties to even:
local function round(num)
return num + (2^52 + 2^51) - (2^52 + 2^51)
end
(Note that the numbers in parentheses are calculated at compilation time; they don't affect runtime).
For example, when the FPU is set to round to nearest or even, this unit test prints "All tests passed":
local function testnum(num, expected)
if round(num) ~= expected then
error(("Failure rounding %.17g, expected %.17g, actual %.17g")
:format(num+0, expected+0, round(num)+0))
end
end
local function test(num, expected)
testnum(num, expected)
testnum(-num, -expected)
end
test(0, 0)
test(0.2, 0)
test(0.4, 0)
-- Most rounding algorithms you find on the net, including Ola M's answer,
-- fail this one:
test(0.49999999999999994, 0)
-- Ties are rounded to the nearest even number, rather than always up:
test(0.5, 0)
test(0.5000000000000001, 1)
test(1.4999999999999998, 1)
test(1.5, 2)
test(2.5, 2)
test(3.5, 4)
test(2^51-0.5, 2^51)
test(2^51-0.75, 2^51-1)
test(2^51-1.25, 2^51-1)
test(2^51-1.5, 2^51-2)
print("All tests passed")
Here's another (less efficient, of course) algorithm that performs the same FPU rounding but works for all numbers:
local function round(num)
local ofs = 2^52
if math.abs(num) > ofs then
return num
end
return num < 0 and num - ofs + ofs or num + ofs - ofs
end
Here's one to round to an arbitrary number of digits (0 if not defined):
function round(x, n)
n = math.pow(10, n or 0)
x = x * n
if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end
return x / n
end
For bad rounding (cutting the end off):
function round(number)
return number - (number % 1)
end
Well, if you want, you can expand this for good rounding.
function round(number)
if (number - (number % 0.1)) - (number - (number % 1)) < 0.5 then
number = number - (number % 1)
else
number = (number - (number % 1)) + 1
end
return number
end
print(round(3.1))
print(round(math.pi))
print(round(42))
print(round(4.5))
print(round(4.6))
Expected results:
3, 3, 42, 5, 5
I like the response above by RBerteig: mils = tonumber(string.format("%.3f", exact)).
Expanded it to a function call and added a precision value.
function round(number, precision)
local fmtStr = string.format('%%0.%sf',precision)
number = string.format(fmtStr,number)
return number
end
Should be math.ceil(a-0.5) to correctly handle half-integer numbers
Here is a flexible function to round to different number of places. I tested it with negative numbers, big numbers, small numbers, and all manner of edge cases, and it is useful and reliable:
function Round(num, dp)
--[[
round a number to so-many decimal of places, which can be negative,
e.g. -1 places rounds to 10's,
examples
173.2562 rounded to 0 dps is 173.0
173.2562 rounded to 2 dps is 173.26
173.2562 rounded to -1 dps is 170.0
]]--
local mult = 10^(dp or 0)
return math.floor(num * mult + 0.5)/mult
end
For rounding to a given amount of decimals (which can also be negative), I'd suggest the following solution that is combined from the findings already presented as answers, especially the inspiring one given by Pedro Gimeno. I tested a few corner cases I'm interested in but cannot claim that this makes this function 100% reliable:
function round(number, decimals)
local scale = 10^decimals
local c = 2^52 + 2^51
return ((number * scale + c ) - c) / scale
end
These cases illustrate the round-halfway-to-even property (which should be the default on most machines):
assert(round(0.5, 0) == 0)
assert(round(-0.5, 0) == 0)
assert(round(1.5, 0) == 2)
assert(round(-1.5, 0) == -2)
assert(round(0.05, 1) == 0)
assert(round(-0.05, 1) == 0)
assert(round(0.15, 1) == 0.2)
assert(round(-0.15, 1) == -0.2)
I'm aware that my answer doesn't handle the third case of the actual question, but in favor of being IEEE-754 compliant, my approach makes sense. So I'd expect that the results depend on the current rounding mode set in the FPU with FE_TONEAREST being the default. And that's why it seems high likely that after setting FE_TOWARDZERO (however you can do that in Lua) this solution would return exactly the results that were asked for in the question.
Try using math.ceil(number + 0.5) This is according to this Wikipedia page. If I'm correct, this is only rounding positive integers. you need to do math.floor(number - 0.5) for negatives.
If it's useful to anyone, i've hash-ed out a generic version of LUA's logic, but this time for truncate() :
**emphasized text pre-apologize for not knowing lua-syntax, so this is in AWK/lua mixture, but hopefully it should be intuitive enough
-- due to lua-magic alrdy in 2^(52-to-53) zone,
-- has to use a more coarse-grained delta than
-- true IEEE754 double machineepsilon of 2^-52
function trunc_lua(x,s) {
return \
((x*(s=(-1)^(x<-x)) \
- 2^-1 + 2^-50 \ -- can also be written as
\ -- 2^-50-5^0/2
- _LUAMAGIC \ -- if u like symmetric
\ -- code for fun
+ _LUAMAGIC \
) *(s) };
It's essentially the same concept as rounding, but force-processing all inputs in positive-value zone, with a -1*(0.5-delta) offset. The smallest delta i could attain is 2^-52 ~ 2.222e-16.
The lua-magic values must come after all those pre-processing steps, else precision-loss may occur. And finally, restore original sign of input.
The 2 "multiplies" are simply low-overhead sign-flipping. sign-flips 4 times for originally negative values (2 manual flips and round-trip to end of mantissa), while any x >= 0, including that of -0.0, only flips twice. All tertiary function calling, float division, and integer modulus is avoided, with only 1 conditional check for x<0.
usage notes :
(1) doesn't perform checks on input for invalid or malicious payload,
(2) doesn't use quickly check for zero,
(3) doesn't check for extreme inputs that may render this logic moot, and
(4) doesn't attempt to pretty format the value
if not exist math.round
function math.round(x, n)
return tonumber(string.format("%." .. n .. "f", x))
end

iOS arc4random NOT beginning from 0

i used arc4random to create a random number, is there a way to tell arc4random to begin at for example -5 instead of 0? because i want to create a random number in the range of
-3,4 to 4,3, im not that good in iOS developing yet, so what other possiblities do i have if that wont work with arc4random (Links are appreciated if theres a guide or something like that)
First of all, use arc4random_uniform to get a random number in the desired absolute range (for -3 to 4 it would be 7): arc4random_uniform(7).
You might also see the form arc4random() % max, but that will introduce a modulo bias making the distribution less random, arc4random_uniform is prefered.
Afterwards, adjust your lower bound:
arc4random_uniform(7) - 3
apple docs
Since arc4random() % n returns an integer from 0..n-1, arc4random() % (n - k) + k returns one from k..n-1. Plug in k=-5. Is that what you need?
If you want it to start at -5 instead of 0, just subtract 5.
If you want to create a random number in the range of -3 to 4, just create a number from 0 to 7 and subtract 3 from it.
The arc4random function gives you a number from zero to 4,294,967,295. To change that to a number from 0 to 7, just divide by 613,566,756. (Or use arc4random_uniform to avoid any bias.)

Insertion of value-pair in Hash for division calculation unexpectedly returning 0 in Rails 3.2.1

In Ruby on Rails 3.2.1, I am iterating over the value pairs of a hash, where trsesh_mode is the key and trsesh_count is the value. I want to divide the trsesh_count for each mode of exercise the user had done by the total number of training sessions that user has completed.
Here is the iteration of the hash:
#trsesh_counts.each do |trsesh_mode, trsesh_count|
count = #user_name.training_sessions.count
#weight = trsesh_count / count
#weight is returning 0 for each value iterated into the calculation.
If I convert this calculation into a string and embed the ruby variables like so:
<% = "(#{trsesh_count}) / (#{count})" %>
... I get this output in my view:
(7) / (20)
(12) / (20)
(1) / (20)
where 20 is the count and the numerators are the iterated trsesh_count values.
If it helps, the trsesh_count values are FixNum.
My question is: Why am I getting 0 for output when performing the calculation above (#weight = ...)? How can I fix this?
You are dividing integer numbers, so that the integer division result is zero.
Force one number to float, and it should work fine, like this:
#weight = trsesh_count.to_f / count

SetRoundMode(rmUp) and rounding "round" values like 10, results in 10,0001 how come?

This code:
SetRoundMode(rmUp);
Memo1.Lines.Add(CurrToStr(SimpleRoundTo(10)));
Results in 10,0001.
I simply don't get it.
I thought that rmUp would do something like, round 10,0001 to 11, but never 10 to 10,0001.
Can anyone explain why this happens?
Thanks.
SimpleRoundTo works like this:
Divide the input value by 10-x, where x is the number of decimal places to preserve in the result.
Add 0.5 to that product.
Truncate the sum.
Multiply by 10-x.
The result is a floating-point value. As with most floating-point values, the result will not be exact, even though in your case you start with an exact value. The number of decimal places specified for SimpleRoundTo is negative, so the divisor in step 1, for your example input, will ideally be 0.01. But that can't be represented exactly as a floating-point number, so when 10 / 0.01 is calculated in step 1, the result is not exactly 1000. The result in step 3 will be exactly 1000, though, so the inexactness of the division isn't important. The inexactness of the multiplication in step 4 is, though. That product won't be exact. It will be slightly higher than 10.
So SimpleRoundTo returns a slightly higher value, and since you've specified that rounding should go up, the conversion of the Extended result of SimpleRoundTo to the Currency input of CurrToStr results in exactly 10.0001.
Currency values are exact; they represent a fixed-point value, an integer scaled by four decimal places.
i'd use the Round( ) function if banker's rounding is ok. it returns an integer.
if you don't like banker's rounding you can use this:
// use this to not get "banker's rounding"
function HumanRound(X: Extended): integer;
// Rounds a number "normally": if the fractional
// part is >= 0.5 the number is rounded up (see RoundUp)
// Otherwise, if the fractional part is < 0.5, the
// number is rounded down
// RoundN(3.5) = 4 RoundN(-3.5) = -4
// RoundN(3.1) = 3 RoundN(-3.1) = -3
begin
// Trunc() does nothing except conv to integer. needed because return type of Int() is Extended
Result := Trunc(Int(X) + Int(Frac(X) * 2));
end;
my posting here is somewhat off-topic but still informative.
i looked into this at length since i needed to not be using banker's rounding. here are my findings. so far as i can see, this still doesn't get rid of banker's rounding
Value Meaning
rmNearest Rounds to the closest value.
rmDown Rounds toward negative infinity.
rmUp Rounds toward positive infinity.
rmTruncate Truncates the value, rounding positive numbers down and negative numbers up.
rmNearest // default
0.500 0
1.500 2
2.450 2
2.500 2
2.550 3
3.450 3
3.500 4
3.550 4
rmDown
0.500 0
1.500 1
2.450 2
2.500 2
2.550 2
3.450 3
3.500 3
3.550 3
rmUp
0.500 1
1.500 2
2.450 3
2.500 3
2.550 3
3.450 4
3.500 4
3.550 4
rmTrunc
0.500 0
1.500 1
2.450 2
2.500 2
2.550 2
3.450 3
3.500 3
3.550 3
uses
math, sysutils, clipbrd;
var
s:string;
procedure trythis(sMode:string);
procedure tryone(d:double);
begin
s:=s+Format('%1.3f %d%s',[d,Round(d),#13+#10]);
end;
begin
s:=s+#13#10+sMode+#13#10;
tryone(0.50);
tryone(1.50);
tryone(2.45);
tryone(2.50);
tryone(2.55);
tryone(3.45);
tryone(3.50);
tryone(3.55);
end;
begin
s:=inttostr(integer(GetRoundMode));
SetRoundMode(rmNearest);
trythis('nearest');
SetRoundMode(rmDown);
trythis('down');
SetRoundMode(rmUp);
trythis('up');
SetRoundMode(rmTruncate);
trythis('trunc');
clipboard.astext:=s;
end.
The return values from calculation SimpleToRound is also a Double and they can never be trusted on rounding. Truncate the value before converting it should do the work!
Memo1.Lines.Add(CurrToStr(Trunc(SimpleRoundTo(10))));
The Ceil() : Integer function should give you the answer you want for values > 0. If < 0 you may need to use floor() instead, depending on desired behaviour.

Resources