Adjusting dates to avoid overlap of days - arrays

If I have three dates, e.g. Jan 1, Jan 25, and Feb 20 but I want the dates to be separated by 30 days, how can i do it?
For example, what I want to do is Jan 1, Jan 30, Feb 29.
I am very new at R but the code should be something like this - If 2nd date is before (1st date+30), then adjust 2nd date to (1st+31) and similarly for 3rd date..
Any help will be much appreciated!

Since you want a fixed distance between each adjacent pair of dates, you don't need to "adjust" any dates; rather, you can just compute the desired date vector from scratch, starting with the first date.
This can actually be done with a single call to the S3 generic seq(), which will dispatch to seq.Date():
seq(as.Date('2000-01-01'),by=30,length.out=3);
## [1] "2000-01-01" "2000-01-31" "2000-03-01"
Also note that you seem to have made an error in deriving your expected dates; 30 days from Jan 1 is Jan 31, not Jan 30.

d1 = as.Date("01-01",format="%m-%d")
d2 = as.Date("01-25",format="%m-%d")
if (abs(as.numeric(difftime(d2,d1)))<30) d2 = d1 + 30
>d2
[1] "2015-01-31"

Related

ARRAY function in SAS after MEAN (by grouping)

There is some homework for SAS and I just can't seem to find the right way to do it. Hopefully, some of you will be able to help.
We start with a table where we have the following variables:
City State Temp January Temp Feb Temp Mar ... Temp Dec
First, we have to calculate the mean temperature (per month, so for 12 different variables) and per state (so there are always a few cities per state).
I used this code:
PROC SORT DATA=Homework;
BY state;
RUN;
PROC MEANS DATA=Homework;
VAR JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC;
BY State
OUTPUT OUT=MTSM (DROP=_type_ _freq_) MEAN=;
RUN;
My result is a table in which I have 53 rows (one per state) and 1 column per month (and a first column for the states of cours). Something like this:
State JAN FEB ... DEC
State1 xjan xfeb ... xdec
State2
...
State53
Now I need to use an Array statement to make a new table in long format:
State Month Mean_temp
State1 JAN xjan
state1 FEB xfeb
. MAR ...
. APR ...
. ... ...
State1 DEC xdec
State 2 JAN ...
...
DEC
...
State53 JAN
FEB
...
Does someone have an idea of how to do this? I'm completely lost.
This is what I tried:
DATA MTSM2;
SET MTSM;
BY state;
ARRAY newvars {1} Mean_Temp;
ARRAY oldvars {1, 12} JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC;
DO Month = JAN to DEC;
DO k=1;
newvars{k} = oldvars{k, Month};
END;
OUTPUT;
END;
KEEP state Month Mean_Temp;
RUN;
I got following error: ERROR: Array subscript out of range at line 30 column 22 :'(
What am I doing wrong? I have been changing this in many ways, but always get the same error.
Thanks in advance!
You can get the table you want by using a more specific output statement in proc means/proc summary:
/*Generate some dummy data*/
data have;
call streaminit(1);
do j = 1 to 10;
do state = 'a', 'b', 'c';
array months[12] m1-m12;
do i = 1 to dim(months);
months[i] = rand('uniform');
end;
output;
end;
end;
drop i j;
run;
proc summary nway data = have;
var m1-m12;
class state;
output out = want(drop = _TYPE_ _FREQ_) mean=;
run;
You are very close.
There is no need to use ARRAY for the new variable since it is just one. There is no need to tell SAS how many variables there are in the array when you have listed the actual variable names. And arrays are indexed by integers, not strings. You can use the VNAME() function to find the name of the variable addressed by the index into the array. The BY statement is not needed.
DATA MTSM2;
SET MTSM;
ARRAY oldvars JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC;
length month $32 mean_temp 8;
DO month_number = 1 to 12 ;
month=vname( oldvars[month_number] );
mean_temp = oldvars[month_number] ;
OUTPUT;
END;
KEEP state Month Mean_Temp;
RUN;
If the homework is to pivot the data, using ARRAY, from the categorically organized layout (state/month/mean) to a wide layout (state/month-1...month-12) you can use BY processing and index determination to fill an array.
Essentially for each BY group there will be one row output.
One way is to use a DOW loop in which the SET statement is inside an explicit loop.
data want(keep=state jan--dec);
do until (last.state);
set have;
by state;
array months jan feb mar apr may jun jul aug sep oct nov dec;
index = (index('JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC', trim(month))+2)/3;
months(index) = mean;
end;
run;
If the data is known to have every month, the index 'lookup' is not needed and can be retrieved directly from the do loop index variable:
data want(keep=state jan--dec);
do _n_ = 1 by 1 until (last.state); /* repurpose _n_ */
set have;
by state;
array months jan feb mar apr may jun jul aug sep oct nov dec;
months(_n_) = mean;
end;
run;
Update
Using array to pivot data from an across layout to a down layout. Iterate a loop over the array elements and output name/value pairs within the loop.
data want (keep=state month percent);
set have;
array months jan feb mar apr may jun jul aug sep oct nov dec;
do _n_ = 1 to dim(months);
month = vname(months(_n_)); /* name */
percent = months(_n_); /* value */
OUTPUT;
end;
run;
Proc TRANSPOSE can perform the same data transformation.
Array based pivoting is very useful when you want to transpose two or more arrays at the same time. An example would be if you had variables
jan_percent to dec_percent and
jan_rating to dec_rating
that you wanted to pivot into a data form of month/percent/rating. Such a transformation with TRANSPOSE requires multiple proc steps (one per array).
Sounds like you simply want to use a Class Statement instead of a By Statement?

Matlab: Daily 3d array to monthly- dealing with alternate days in a month and leap years - How to do it?

In matlab I have a 720x360x365 matrix (let's call it A) of daily precipitation for one year. 365 stands for days in a year. I need to write a code to convert these daily data to the monthly sum. If I start from January, I need to do mean (A,3) of the first 31 days, then the mean (A,3) of February, the next 28 or 29 days. Because the days alternate between 31 and 30 (and 28 or 29 for February), I don't know how to write a code to do this.
please help me I don't know how to do it.
thank you
You can use mat2cell to divide your data in cells per month. First make a vector with the number of days per month (not taking into account leap years), and then use this to divide the data. Then you can use cellfun on each cell (i.e. month) to get any metric you define per month:
data = rand(720, 360, 365);
days_per_month = [31 28 31 30 31 30 31 31 30 31 30 31];
% divide months in cells
data_cell = mat2cell(data, size(data,1), size(data,2), days_per_month);
mean_cell = cellfun(#(A) mean(A,3), data_cell, 'UniformOutput', false)
To use this in a loop, and account for leap years, you can use the function leapyear(year):
days_per_month = [31 28 31 30 31 30 31 31 30 31 30 31];
years = 1984:2015
for k = 1:numel(years)
if leapyear(years(k))
days_per_month(2) = 29;
else
days_per_month(2) = 28;
end
% rest of what you want to do
end

SSRS. How to group in a group?

I have SSRS report like below with Boolean parameter to show 12h view or 24h view. To fit report into single screen the 24h report need to group by every 2hr.
07:00 08:00 09:00 10:00 11:00 12:00 13:00 14:00 ...
Line 1 25 30 24 26 25 25 30 30 ...
08:00 10:00 12:00 14:00 ...
Line 1 55 50 50 60 ...
The query for the dataset is:
SELECT LineID
,Hour
,HourValue
,Target
FROM vwData
ORDER BY LineID, CASE WHEN [Hour] > 6 THEN - 1 ELSE [Hour] END
How can I achieve this?
This declares your bit variable (which should be true when they want the 24 hour view - false when 12 hour)
DECLARE #24Hour bit = 0
SELECT CASE WHEN #24Hour = 0
THEN Hour
ELSE Hour + (Hour % 2)
END AS [HourGroup]
,SUM(Target) AS [TargetTotal]
FROM vwData
GROUP BY CASE WHEN #24Hour = 0
THEN Hour
ELSE Hour + (Hour % 2)
END
If they want the 24 hour view, we make hour = hour + hour % 2. (7 = 8, 8=8, 9=10, etc., etc.). If you had a more complex query, I would suggest reading up on cross apply, but this is so simple I think this will suffice. The grouping by makes sure to aggregate the REAL 7 and REAL 8 hour records (which will both be returned as "8", if using the 24 hour view). If you don't group your results, you will get two 8 oclock records - one with the REAL 7 hour total and one with the REAL 8 hour total.
EDIT:
Since you didn't include the schema of your DB, I'm guessing that 'Target' is the value being summated, but it could just as easily be 'HourValue'. Furthermore, I have no idea why you would need LineID, so I omitted it from my answer. But you can easily modify that if it's inaccurate. In the future, you should provide some sample data and your database schema so that others aren't forced to make assumptions or guess.
You could add a calculated field with a value given by something like this: `Fields!Hour.Value + Fields!Hour.Value Mod 2' and then group on that field, using a parameter to choose the Group By field in the report (Your new field or the actual hour value).

how to subtract adjacent columns in an ssrs matrix

I have an ssrs matrix which looks like the one below :
Month(Columns)
Product(Rows) Sales(Data)
The output looks something like this :
June July August Sept Oct
ABC 34 34 23 22 67
DEF 33 21 32 22 14
I want an output that looks like this :
June July June-July Aug July-Aug Sept Aug-Sept Oct Sept-Oct
ABC 34 34 0 23 11 22 1 67 45
DEF 33 21 12 32 11 22 10 14 8
I tried doing something like this :
Month(Columns) Change
Product(Rows) Sales(Data) Expression
The expression looks something like this :
=Sum(IIF(Fields!MONTH.Value=Fields!MONTH.Value,Fields!Products.Value,Nothing))-
Sum(IIF(Fields!MONTH.Value=Fields!MONTH.Value - 1,Fields!Products.Value,Nothing))
But it doesnt work . I want to see the output as shown above . Please let me know.
Hey Sam ,
With the solution you mentioned :
I see an output like this :
June Garbage July July-June Aug Aug-Jul
ABC 34 xx 34 0 23 11
DEF 33 xx 21 12 32 11
Is there a way we can remove the column with the garbage values ?
Hey Sam , I tried your code. Now I have a big white space all along the column. Is there a way I can hide the wide space too ?
If you are grouping your columns by month then you don't need to use the SumIif
You can use a expression such as =Sum(Fields!Products.Value) to get the sum of all products in that particular month. If you want to see the difference between the current month and the previous month then if you enter the below expression in a cell within the month column group...
=Iif(Previous(Fields!MONTH.Value) = Nothing, 0,
Sum(Fields!Products.Value) - Previous(Sum(Fields!Products.Value)))
You need the null check in this instance as the first month will return nothing for previous.
If you have overlapping row and column row groups (which I believe you do) then you won't be able to use Previous as it isn't supported :-(
I think that the only solution is to use some custom code.
There is a link here
Public Shared previous as Integer
Public Shared current as Integer
Public Shared Function GetCurrent(Item as Integer) as Integer
previous=current
current=Item
return current
End Function
Public Shared Function GetPrevious()
return previous
End Function
Then your usage would be something like
=Code.GetCurrent(Sum(Fields!Products.Value)) - Code.GetPrevious()
I found a way to calculate the differences between Matrix columns using the 'previous' function by adding the column grouping name.
=Previous(Sum(Fields!AMOUNT.Value),"PeriodGroupName")
Look here for a little more detail.
http://www.tricks-and-tips.nl/tips-and-tricks/sql/ssrs/ssrs-matrix-compare-column-values
And here for the documentation.
https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms156372(v=sql.105)

How to check if week number is even or odd in ANSI C?

I`ve a small app that returns is week even or not.
time_t now = time(0);
tm *ltm = localtime(&now);
int twin=(ltm->tm_yday/7)%2
But independently from the 1st day of the year so it returns
mon, thu, we, etc
0,1,1,1,1,1,1
in the next week
1,0,0,0,0,0,0
In the next year
mon, thu, we, etc
0,0,1,1,1,1,1
in the next week
1,1,0,0,0,0,0
and so on..
Twin- if number modulo 2 = 0
So I have to add shift to change week number in each sunday or monday. Any suggestions?
You are assuming that first week has exactly 7 days which is incorrect.
For example Jan 1st 2013 was Tuesday, so the first week is only 5 days long.
How about using strftime? Something like:
time_t now = time(0);
tm *ltm = localtime(&now);
char weekNr[3];
strftime(weekNr, sizeof(weekNr), "%W", ltm);
int isOdd = atoi(weeknr) % 2;
What you call twin, in English is usually called even.
About your question, the issue here is that you are not calculating the week number correctly: you are simply dividing by 7, and that's not enough because the start of year and the start of week vary each year.
Moreover, there are several different ways to decide which one is week 1. See for example this code, to get started.
UPDATE: Copying shamelessly from the eglibc source code:
1) The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01 (strftime("%U")):
tp->tm_yday - tp->tm_wday + 7) / 7
2) The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01 (strftime("%W")):
(tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7
3) The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the new year (strftime("%V")):
Well this is complicated... so you are better with the idea by #MaikuMori of using strftime``, but with"%V", and then parse the result, withatoi()`.

Resources