views:

223

answers:

8

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();
+1  A: 

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.

Nicolas78
Break; added thanks. See my original update for new time
Chris
+4  A: 

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

}
Martin Harris
Thanks Martin, orderedData[0].Date.Year is giving me an error:Error 2 Cannot apply indexing with [] to an expression of type 'System.Linq.IOrderedEnumerable<AnonymousType#1>'
Chris
Try orderedDate.First().Date.Year instead.
Jesper Palm
A trivial matter, but why store End_Of_Week in a string when you end up comparing it to a DayOfWeek enumeration anyways? Better off with DayOfWeek End_Of_Week = DayOfWeek.Sunday; ... LoopDate_Begin.DayOfWeek != End_of_Week
DonaldRay
+1  A: 

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 ;)

Nicolas78
I agree; and can't understand how solutions are being upvoted that are "optimizing" the looping approach. Zeller's congruence for the win!
Carl Smotricz
+1  A: 

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

Jacob G
If that is an option you can use built in functions for that: CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(someDate, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Sunday)
Jesper Palm
Very nice. Didn't even know that existed.
Jacob G
A: 

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);
}
mjeanes
+3  A: 

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.

Jesper Palm
Jesper I'm unsure if this will produce the correct results because of the CalendarWeekRule. Please see my update.
Chris
@Chris: What if you use CalendarWeekRule.FirstDay ?
Jesper Palm
Thanks Jesper, checked the first days of the last few years and values are all correct!
Chris
+1  A: 

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.

Paul E.
A: 

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);
Robaticus