Chris
Chris

Reputation: 1217

"Week of the year" Algorithm needs improvements

I have an algorithm which scans through data read from a .csv file(approx 3700 lines) and assess's which trading week of the year each entry is in by running a count++ for every Sunday of that year and assigning the count value as the trading week when the date falls within that week.

It's working but performance is lagging. It is the 3rd function running using Task.Factory.StartNew (I have also tried parallel.Invoke).

Results of timing tests.

before: 00:00:05.58

after: 00:00:23.27

UPDATE

Added break after each trading week is set. Time improved but still slow.

new time: 00:00:15.74

For our purposes the 1st week of the year is week 1(not 0) and is defined as from the first day of the year until the Sunday. If the first day of the year is a Sunday the length of week 1 is 1 day.

private void SetDefiniteWeeks()
        {
            string FileLoc = FilePath + Market + ".csv";
            string[] Data = File.ReadAllLines(FileLoc);
            var FileData = from D in Data
                           let DataSplit = D.Split(',')
                           select new
                           {
                               Date = DateTime.Parse(DataSplit[0]),
                               ClosingPrice = double.Parse(DataSplit[4])
                           };

            //assign each date to it's relevant week
            TradingWeek TW;
            List<TradingWeek> tradingWeek = new List<TradingWeek>();
            foreach (var pe in FileData)
            {
               // DateTime dt = pe.Date;
                int Year = pe.Date.Year;
                string End_of_Week = "Sunday";
                int WeekCount = 0;

                DateTime LoopDate_Begin = new DateTime(Year,1,1);
                DateTime LoopDate_End = new DateTime(Year,12,31);
                do
                {
                    if (LoopDate_Begin.DayOfWeek.ToString() == End_of_Week)
                    {
                        WeekCount++;
                        if (LoopDate_Begin.DayOfYear > pe.Date.DayOfYear && LoopDate_Begin.DayOfYear < (pe.Date.DayOfYear + 7))
                        {
                            TW = new TradingWeek { Week = WeekCount, Date = pe.Date };
                            tradingWeek.Add(TW);
                            break;
                        }
                    }
                    LoopDate_Begin = LoopDate_Begin.AddDays(1);

                } while (LoopDate_Begin.Date.ToString() != LoopDate_End.Date.ToString());

            }

        }

Please help.

UPDATE

NEW TIME

00:00:06.686

A vast improvement. Thanks all for your help.

Revised code:

    CalendarWeekRule cw = CalendarWeekRule.FirstDay;
    var calendar = CultureInfo.CurrentCulture.Calendar;
    var trad_Week = (from pe in FileData
                        select new TradingWeek
                        {
                        Date = pe.Date,
                        Week = (calendar.GetWeekOfYear(pe.Date, cw,DayOfWeek.Sunday))
                        }).ToList();

Upvotes: 4

Views: 834

Answers (8)

mjeanes
mjeanes

Reputation: 1626

You don't need to count at all - just do a quick calculation. This assumes that a partial week at the start of the year is week 1 and week 2 begins on the first Monday.

List<TradingWeek> tradingWeek = new List<TradingWeek>();
foreach (var pe in FileData)
{
    var date = pe.Date;
    while (date.DayOfWeek != DayOfWeek.Sunday)
        date = date.AddDays(1);
    var week = date.DayOfYear/7+1;
    var TW = new TradingWeek {Week = week, Date = pe.Date};
    tradingWeek.Add(TW);
}

Upvotes: 0

Jesper Palm
Jesper Palm

Reputation: 7248

Im not sure if this is what you want but after reading the comments I got the feeling that this might work (?)

var calendar = CultureInfo.CurrentCulture.Calendar;
var tradingWeek = (from pe in FileData
                  select new TradingWeek
                  {
                    Date = pe.Date,
                    Week = calendar.GetWeekOfYear(pe.Date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
                  }).ToList();

Edit: Changed to CalendarWeekRule.FirstDay since it's (more?) what OP is looking for.

Upvotes: 4

Robaticus
Robaticus

Reputation: 23157

There's a built-in feature to get the week of the year based on the date in .NET. An example is shown below, but it may need some tweaking to fit your business scenario:

System.Globalization.CultureInfo myCI = new System.Globalization.CultureInfo("en-US");

int week = myCI.Calendar.GetWeekOfYear(DateTime.Now.ToUniversalTime(), System.Globalization.CalendarWeekRule.FirstFourDayWeek, System.DayOfWeek.Sunday);

Upvotes: 0

Paul E.
Paul E.

Reputation: 269

Following @nicolas78's response, something like this should work

int Year = pe.Date.Year;
DateTime Year_Begin = new DateTime(Year,1,1);
int Jan1DayOfWeek = Year_Begin.DayOfWeek;

foreach (var pe in FileData)
{
    int WeekCount = (pe.Date.DayOfYear - Jan1DayOfWeek) % 7 + 1;
    TradingWeek TW = new TradingWeek { Week = WeekCount, Date = pe.Date };
    tradingWeek.Add(TW);
}

Depending on how DayOfWeek and DayOfYear count, that is from 0 or 1, and how your mod operation work, you may need to tweak the WeekCount computation a bit.

Upvotes: 1

Jacob G
Jacob G

Reputation: 3665

Can you get rid of your do loop altogether by calculating the Week Number directly? Something like the accepted answer here.

Upvotes: 1

Nicolas78
Nicolas78

Reputation: 5144

You might even go for a loopless approach by dividing the number of days since your starting week by 7 - and doing some cleaning up work ;)

Upvotes: 1

Martin Harris
Martin Harris

Reputation: 28627

Three quick thoughts:

  • Why are you only adding one day each time and checking to see if it's Sunday. Surely once you have found your first Sunday you can add seven days to find the next one?

  • If you order your pes by DateTime before you start then you don't need to restart at the beginning of the year for each one, you can pick up where you left off.

  • As Nicolas says, break after adding the trading week. No need to go through the rest of the year after you already know what the answer is.

I guess you'll end up with something like this (may or may not actually work, but should be close enough)

TradingWeek TW;
List<TradingWeek> tradingWeek = new List<TradingWeek>();
string End_of_Week = "Sunday";
var orderedData = FileData.OrderBy(x => x.Date)
DateTime LoopDate_Begin = new DateTime(orderedData[0].Date.Year,1,1);
int WeekCount = 1; 

while (LoopDate_Begin.DayOfWeek.ToString() != End_of_Week)
{
    LoopDate_Begin = LoopDate_Begin.AddDays(1);
}

foreach (var pe in orderedData)
{
    do
    {           
        if (LoopDate_Begin.DayOfYear > pe.Date.DayOfYear && LoopDate_Begin.DayOfYear < (pe.Date.DayOfYear + 7))
        {
            TW = new TradingWeek { Week = WeekCount, Date = pe.Date };
            tradingWeek.Add(TW);
            break;
        }
        WeekCount++;
        LoopDate_Begin = LoopDate_Begin.AddDays(7);

    } while (true); //need to be careful here

}

Upvotes: 4

Nicolas78
Nicolas78

Reputation: 5144

if I get you correctly, you don't need to look any further as soon as you've added your TradingWeek

So, you can

break;

after

tradingWeek.Add(TW);

You could then even leave out the

&& LoopDate_Begin.DayOfYear < (pe.Date.DayOfYear + 7)

condition since the first part is going to be true only once: for your desired interval.

Upvotes: 1

Related Questions