Bad calculations after Python to C# port - c#

I'm working on a time-decay algorithm for a post system based on Reddit's model here:
http://amix.dk/blog/post/19588
My working port is here:
public class Calculation
{
protected DateTime Epoch = new DateTime(1970, 1, 1);
protected long EpochSeconds(DateTime dt)
{
var ts = dt.Subtract(Convert.ToDateTime("1/1/1970 8:00:00 AM"));
return ((((((ts.Days * 24) + ts.Hours) * 60) + ts.Minutes) * 60) + ts.Seconds);
}
protected int Score(int upVotes, int downVotes)
{
return upVotes - downVotes;
}
public double HotScore(int upVotes, int downVotes, DateTime date)
{
var s = Score(upVotes, downVotes);
var order = Math.Log(Math.Max(Math.Abs(s), 1), 10);
var sign = Math.Sign(s);
var seconds = EpochSeconds(date) - 1134028003;
return Math.Round(order + sign * ((double)seconds / 45000), 7);
}
}
Based on the model output from the link provided, I should see gradual decay at 0-13 hours, and sharp decay after that.
What I'm seeing is very homogeneous decay, and scores much higher than the output from the original code (original code: 3480-3471).
Here is how I'm testing:
Calculation c = new Calculation();
double now = c.HotScore(100, 2, DateTime.Now);
double fivehoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-5));
double tenhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-10));
double elevenhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-11));
double twelvehoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-12));
double thirteenhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-13));
double fiftyhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-50));
double onehundredhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-100));
Console.WriteLine(now.ToString());
Console.WriteLine(fivehoursago.ToString());
Console.WriteLine(tenhoursago.ToString());
Console.WriteLine(elevenhoursago.ToString());
Console.WriteLine(twelvehoursago.ToString());
Console.WriteLine(thirteenhoursago.ToString());
Console.WriteLine(fiftyhoursago.ToString());
Console.WriteLine(onehundredhoursago.ToString());
Console.ReadLine();
Output values:
now: 4675.2993816
five hours: 4674.8993816
ten hours: 4674.4993816
eleven hours: 4674.4193816
twelve hours: 4674.3393816
thirteen hours: 4674.2593816
fifty hours: 4671.2993816
one-hundred hours: 4667.2993816
Clearly it's SORT of working right, but something is off. It could be related to the lack of true *nix Epoch support, or the lack of analogous microseconds calculation, but something isn't quite right.
Possible reference resources:
http://blogs.msdn.com/b/brada/archive/2004/03/20/93332.aspx
http://codeclimber.net.nz/archive/2007/07/10/convert-a-unix-timestamp-to-a-.net-datetime.aspx

Your primary problem is that the hot algorithm is time dependent. Your calculating the hot score at DateTime.Now, whereas the article was written on 23. Nov 2010 (look at the bottom of the article).
With some trial and error, it seems the data was calculated at approximately 2010-11-23 07:35. Try using that value rather than DateTime.Now, and you should get about the same results as the data in the graph shown.
Mind you, you could make the following improvements to your code:
public class Calculation
{
private static readonly DateTime Epoch = new DateTime(1970, 1, 1);
private double EpochSeconds(DateTime dt)
{
return (dt - Epoch).TotalSeconds;
}
private int Score(int upVotes, int downVotes)
{
return upVotes - downVotes;
}
public double HotScore(int upVotes, int downVotes, DateTime date)
{
int s = Score(upVotes, downVotes);
double order = Math.Log(Math.Max(Math.Abs(s), 1), 10);
int sign = Math.Sign(s);
double seconds = EpochSeconds(date) - 1134028003;
return Math.Round(order + sign * seconds / 45000, 7);
}
}
My results:
3479.0956039
3478.6956039
3478.2956039
3478.2156039
3478.1356039
3478.0556039
3475.0956039
3471.0956039
Changes:
Used the declared Epoch rather than a convert of 1970-01-01 08:00:00 (I think 08:00 is a mistake).
You can subtract two dates using a - b; which is the same as a.Subtract(b) but more succinct and it mirrors the original Python code.
A timespan does give you microsecond precision (Ticks are the smallest unit and equal 100 nanoseconds).
Also, TotalSeconds gives you the total number of seconds within a time span; no need to recalculate that. The fractional part even gives you your microsecond precision.
By returning double from EpochSeconds, you keep this precision.
Made the data types explicit rather than var to clearly indicate what variable is what (they match the method signatures, so no implicit upcasting).
Changed unneeded protected to private and made the Epoch a constant.

Related

How to round to any number in C#?

I am in the middle of making a calculator in C# and I want to round to nearest 1, 10, 100, etc and also like nearsest 0.1, 0.001, etc. I have seen other projects that tell me how to do it but I tried and they don't seem to work.
I've tried:
textBox1.Text = Convert.ToString(Math.Round(Convert.ToDouble(Label1.Text), Convert.ToInt32(textBox1.Text), MidpointRounding.AwayFromZero));
and...
textBox1.Text = Convert.ToString(Math.Round(Convert.ToDouble(Label1.Text), Convert.ToInt32(textBox1.Text)));
and...
textBox1.Text = Convert.ToString(Convert.ToInt32(Label1.Text) / Convert.ToInt32(textBox1.Text) * Convert.ToInt32(textBox1.Text));
Math.Round has overloads that allow you to round to a particular decimal.
e.g.
Math.Round(0.05, 1, MidpointRounding.AwayFromZero);
will result in 0.1
If you want to round to the nearest 10, 100, etc, you will need to do a bit more math.
Math.Round((double)50 / 100, MidpointRounding.AwayFromZero) * 100;
results in 100, for rounding to the nearest hundred, while
Math.Round((double)55 / 10, MidpointRounding.AwayFromZero) * 10;
will get you to the nearest 10, in this case 60.
You can make an Extension method to do it, using the same idea from Jonathon's answer. You divide the input by the interval you choose, round it using Math.Round, and then multiply the rounded number by the interval.
static class Extensions
{
public static int RoundToInterval(this int i, int interval)
{
if (interval == 0)
{
throw new ArgumentException("The specified interval cannot be 0.", nameof(interval));
}
return ((int)Math.Round((double)i / (double)interval)) * interval;
}
}
Calling it will look like so.
int input = 13;
var result = input.RoundToInterval(10);
The result of the previous example call will be 10. Change input to 16, and the result will be 20. If you pass 100 as the argument to RoundToInterval, the result will be 0, and 51 (and 50 with the default MidpointRounding choice) will give the result of 100.

Incorrect linear interpolation with large x values using Math.Net Numerics

I'm trying to use Math.NET Numerics to do interpolation of a DateTime - Value series. I started off with linear interpolation, but am getting some very off looking results.
Running this test:
public class script{
public void check_numerics()
{
var ticks = DateTime.Now.Ticks;
Console.WriteLine("Ticks: " + ticks);
var xValues = new double[] { ticks, ticks + 1000, ticks + 2000, ticks + 3000, ticks + 4000, ticks + 5000 };
var yValues = new double[] {0, 1, 2, 3, 4, 5};
var spline = Interpolate.LinearBetweenPoints(xValues, yValues);
var ticks2 = ticks;
for (int i = 0; i < 10; i++)
{
ticks2 += 500;
Console.WriteLine(spline.Interpolate(ticks2));
}
}
}
This gives:
Ticks: 635385235576843379
0.5
1
1.5
2
2.42857142857143 // this should be 2.5
3
3.5
4
4.5
5
Notice that 2.4285 is fairly wrong. At a different time (different ticks value) a different value will be "wrong". Is there a "bug" with large x values in Math.NET or am I expecting too much?
Just confirming the comments above as the maintainer of Math.NET Numerics:
The distance (epsilon) between the closest numbers of this magnitude that can be represented at double precision is 128:
Precision.EpsilonOf(ticks); // 128
This means that if you add or substract 128/2-1 = 63 from this number, you get back exactly the same number:
long ticks = DateTime.Now.Ticks // 635385606515570758
((long)(double)ticks) // 635385606515570816
((long)(63+(double)ticks)) // 635385606515570816
((long)(-63+(double)ticks)) // 635385606515570816
((long)(65+(double)ticks)) // 635385606515570944
((long)(-65+(double)ticks)) // 635385606515570688
The incremental steps of 500 are very close to these 128 and effectively get rounded to multiples of 128 (e.g. 512), so it's not surprising that there will be some artifacts like this.
If you reduce the time precision to milliseconds by dividing the ticks by 10000, as suggested by James, you get an epsilon of 0.0078125, and accurate results even for steps of 1 instead of 500.
Precision.EpsilonOf(ticks/10000); // 0.0078125

Converting a double into date time

I have a user input values in my table.
Here if user enters 3.2, it mean 3 hours and 20 min.
I am showing the total hours that has been input by the user in the entire week.
Inputs are:
Sun : 3.2
Mon : 4.5
Tue: 5.0
Now, 3.2 + 4.5 + 5.0 = 12.70 which would mean 12 hours and 70 min.
However, I want the result to be 13.10 (which is 13 hours and 10 min) instead of 12.70.
I need the total in a select query which is binding my grid with the rest of the data.
Currently i am using the sum function in the select query along with other columns.
How do I do this?
Your input format won't work at all.
You are lucky it does in your example but in most cases it just won't. For instance, if 2 persons input "1 hour and 50 minutes" :
1.5 + 1.5 = 3.0
You cannot read it as : "three hours" since in reality it is "3 hours and 40 minutes".
As soon as the sum of "minutes" is greater thant 0.99, your are wrong.
But in the few lucky cases, you can do some arithmetic (if you want a result in the same "double" format as your input)?
var inputList = new List<double>() {3.2, 4.5, 5.0};
double total = inputList.Sum();
int baseHours = (int)Math.Floor(total);
int realBaseHours = (int)inputList.Sum(d => Math.Floor(d));
if (baseHours > realBaseHours)
throw new Exception("All hell breaks loose!");
int baseMinutes = (int)(total * 100.0 - baseHours * 100.0);
int finalHours = baseHours + baseMinutes / 60;
int finalMinutes = baseMinutes % 60;
double result = finalHours + finalMinutes / 100.0;
It's not good to saving times as a double format, but for your question:
Get all times as array of double values and do some arithmetic:
double[] times = { 3.2, 4.5, 5.0 };
int hours = 0;
int minuts = 0;
string[] values;
foreach (double t in times)
{
values = t.ToString("#.00").Split('.');
hours += int.Parse(values[0]);
minuts += int.Parse(values[1]);
}
hours += minuts / 60;
minuts += minuts % 60;
It work for all kind of times as double format.

DateTime Round Up and Down

Ive been looking for a proper rounding mechanism but nothing I find seems to be exactly what I need.
I need to round up and round down seperately and I also need to account for the the case when its already rounded.
I need the following rounding to happen
5:00 -> RoundDown() -> 5:00
5:04 -> RoundDown() -> 5:00
5:09 -> RoundDown() -> 5:00
5:10 -> RoundDown() -> 5:10
4:00 -> RoundUp() -> 4:00
4:50 -> RoundUp() -> 4:50
4:51 -> RoundUp() -> 5:00
4:56 -> RoundUp() -> 5:00
Basically I need it to RoundUp() or RoundDown() to the nearest 10 minutes explicitly but it should also leave time untouched if it already is in a multiple of 10 minutes. Also I'd like to truncate any seconds to that they have no effect on the rounding procedure
4:50:45 -> 4:50:00 -> RoundUp() -> 4:50
Does anyone have any handy code to accomplish this.
I found this code somewhere but it rounds 5:00 -> RoundUp() -> 5:10 rather than leaving it intact because its already a multiple of 10 and needs no rounding. Also Im not sure how seconds would effect it
public static DateTime RoundDateTime(this DateTime dt, int minutes, RoundingDirection direction)
{
TimeSpan t;
switch (direction)
{
case RoundingDirection.Up:
t = (dt.Subtract(DateTime.MinValue)).Add(new TimeSpan(0, minutes, 0)); break;
case RoundingDirection.Down:
t = (dt.Subtract(DateTime.MinValue)); break;
default:
t = (dt.Subtract(DateTime.MinValue)).Add(new TimeSpan(0, minutes / 2, 0)); break;
}
return DateTime.MinValue.Add(new TimeSpan(0,
(((int)t.TotalMinutes) / minutes) * minutes, 0));
}
Hope someone can edit that method to make it work for me. Thanks
This will let you round according to any interval given.
public static class DateTimeExtensions
{
public static DateTime Floor(this DateTime dateTime, TimeSpan interval)
{
return dateTime.AddTicks(-(dateTime.Ticks % interval.Ticks));
}
public static DateTime Ceiling(this DateTime dateTime, TimeSpan interval)
{
var overflow = dateTime.Ticks % interval.Ticks;
return overflow == 0 ? dateTime : dateTime.AddTicks(interval.Ticks - overflow);
}
public static DateTime Round(this DateTime dateTime, TimeSpan interval)
{
var halfIntervalTicks = (interval.Ticks + 1) >> 1;
return dateTime.AddTicks(halfIntervalTicks - ((dateTime.Ticks + halfIntervalTicks) % interval.Ticks));
}
}
To take care of truncating the seconds, I would simply subtract the seconds and milliseconds from the date-time before sending them into the rounding functions.
How about:
case RoundingDirection.Up:
t = dt.AddMinutes((60 - dt.Minute) % 10);
case RoundingDirection.Down:
t = dt.AddMinutes(-dt.Minute % 10);
Demo: http://ideone.com/AlB7Q
Here is a fast way to truncate (round down)
var now = DateTime.Now;
var nowTicks = now.Ticks;
//removing the nanoseconds, miliseconds, and seconds from the nowTicks
var lastMinute = new DateTime(nowTicks - (nowTicks % (1000*1000*10*60)));
This function will round up or down to the nearest interval (minutes).
private static DateTime NormalizeReadingInterval(DateTime originalTime, int interval)
{
if (originalTime.Minute % interval == 0) return originalTime;
var epochTime = new DateTime(1900, 1, 1);
var minutes = (originalTime - epochTime).TotalMinutes;
var numIntervals = minutes / interval;
var roundedNumIntervals = Math.Round(numIntervals, 0);
return epochTime.AddMinutes(roundedNumIntervals * interval);
}
Another approach avoiding arithmetic using type long.
Using integer division, where a & b are positive integers:
a/b // rounding down
(a+b-1)/b // rounding up
((2*a)+b)/(2*b) // rounding to the nearest (0.5 up)
To round up:
public static DateTime UpToNearestXmin( DateTime dt, int block )
{
int a = dt.Minute;
int b = block;
int mins = block * (( a + b - 1 ) / b );
return new DateTime( dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0 ).AddMinutes( mins );
}
To round down or to nearest, change the mins calculation as appropriate.
The minutes are rounded. The seconds & milliseconds are zeroed which is expected behaviour.

Is there a better way in C# to round a DateTime to the nearest 5 seconds?

I want to round a DateTime to the nearest 5 seconds. This is the way I'm currently doing it but I was wondering if there was a better or more concise way?
DateTime now = DateTime.Now;
int second = 0;
// round to nearest 5 second mark
if (now.Second % 5 > 2.5)
{
// round up
second = now.Second + (5 - (now.Second % 5));
}
else
{
// round down
second = now.Second - (now.Second % 5);
}
DateTime rounded = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, second);
Please note that I've found these two previous questions, however they truncate rather than round the time.
(Sorry for the resurrection; I recognize it's an old and answered question - just adding some extra code for Google's sake.)
I started with JayMcClellan's answer, but then I wanted it to be more generic, rounding to arbitrary intervals (not just 5 seconds). So I ended up leaving Jay's method for one that uses Math.Round on ticks and put it into an extension method that can take arbitrary intervals and also offers the option of changing the rounding logic (banker's rounding versus away-from-zero). I'm posting here in case this is helpful to someone else as well:
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType) {
return new TimeSpan(
Convert.ToInt64(Math.Round(
time.Ticks / (decimal)roundingInterval.Ticks,
roundingType
)) * roundingInterval.Ticks
);
}
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval) {
return Round(time, roundingInterval, MidpointRounding.ToEven);
}
public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval) {
return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks);
}
It won't win any awards for bare efficiency, but I find it easy to read and intuitive to use. Example usage:
new DateTime(2010, 11, 4, 10, 28, 27).Round(TimeSpan.FromMinutes(1)); // rounds to 2010.11.04 10:28:00
new DateTime(2010, 11, 4, 13, 28, 27).Round(TimeSpan.FromDays(1)); // rounds to 2010.11.05 00:00
new TimeSpan(0, 2, 26).Round(TimeSpan.FromSeconds(5)); // rounds to 00:02:25
new TimeSpan(3, 34, 0).Round(TimeSpan.FromMinutes(37); // rounds to 03:42:00...for all your round-to-37-minute needs
The Ticks count of a DateTime represents 100-nanosecond intervals, so you can round to the nearest 5 seconds by rounding to the nearest 50000000-tick interval like this:
DateTime now = DateTime.Now;
DateTime rounded = new DateTime(((now.Ticks + 25000000) / 50000000) * 50000000);
That's more concise, but not necessarily better. It depends on whether you prefer brevity and speed over code clarity. Yours is arguably easier to understand.
Like you mentioned, it's fairly easy to truncate. So, just add 2.5 seconds, then truncate down.
I can't think of a better way, although I would probably factor out the round method:
static int Round(int n, int r)
{
if ((n % r) <= r / 2)
{
return n - (n % r);
}
return n + (r - (n % r));
}
Also, % returns an int, so comparing it to 2.5 strikes me as a little odd, even though it is correct. I'd use >= 3.
How about this (blending a few answers together)? I think it conveys the meaning well and should handle the edge cases (rounding to the next minute) elegantly due to AddSeconds.
// truncate to multiple of 5
int second = 5 * (int) (now.Second / 5);
DateTime dt = new DateTime(..., second);
// round-up if necessary
if (now.Second % 5 > 2.5)
{
dt = dt.AddSeconds(5);
}
The Ticks approach as shown by Jay is more concise, but may be a bit less readable. If you use that approach, at least reference TimeSpan.TicksPerSecond.
I couldn't recognize the difference between C# and a bar of soap (well, I couldn't when I originally wrote this answer, things have changed quite a bit in the years since) but, if you're looking for a more concise solution, I would just put the whole thing in a function - there's little that will be more concise in your code than a simple call to said function:
DateTime rounded = roundTo5Secs (DateTime.Now);
Then you can put whatever you want in the function and just document how it works, such as (assuming these are all integer operations):
secBase = now.Second / 5;
secExtra = now.Second % 5;
if (secExtra > 2) {
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
secBase + 5);
}
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
secBase);
You may also need some extra checks if secBase goes to 60 (unless C# DateTime objects are smart enough to bump up the minute (and hour if minute goes to 60, and so on).
Technically, you can never correctly round to an odd interval given only seconds.
2, 4, 6, 8, 10 <-- are no problem
If you are 'distributing' times in intervals and if the jitter is low, truncation is a lot
more tractable.
If you can pass milliseconds and round at a 500mS mark, you will be able to to odd
seconds and also slash the effect of jitter way down or eliminate it entirely.
Most simple and accurate one-liner:
private static DateTime QuantizeToEachNthSecond(DateTime dateTime, int nthSecond = 5)
{
return dateTime.AddTicks(-(dateTime.Ticks % (nthSecond * TimeSpan.TicksPerSecond)));
}
so, if you like each 5th second, if will be truncated to e.g. "10:12:02" -> "10:12:00", "10:12:08" -> "10:12:05" and so on.

Categories

Resources