SAS creating a season variable from a date variable [closed] - date

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am trying to create a season variable from a date variable so if the date is 09/09/2010 then the season would be "Fall".
Can anyone help?
Thanks

Assuming this is based on the month/day calculation. It depends on the exact logic, but something like this would work.
Use MONTH() to calculate the month of the year and then use that to assign the season. If you need to account for specific days, ie Winter is Dec 22 to Mar 22 then you can also use the DAY() function to get the day of the month.
data want;
set sashelp.stocks (where = stocks='IBM');
length Season $10.;
if month(date) in (3, 4, 5) then season = 'Spring';
else if month(date) in (6, 7, 8) then season = 'Fall';
else if ....;
else if ....;
else season = 'CHECK ME';
run;
Almost all SAS installations ship with the SASHELP library so you should be able to run this automatically.

You probably want to create a custom format based on a table of season start dates downloaded from the interwebs.
Or you could use a web api such as https://www.timeanddate.com/services/api/holiday-api.html
Or if you are a computational masochist, deal with the computed equinox based on lat and long.
From "https://aa.usno.navy.mil/faq/docs/GAST.php" (the page has a security certificate error)
Sidereal time is a system of timekeeping based on the rotation of
the Earth with respect to the fixed stars in the sky. More
specifically, it is the measure of the hour angle of the vernal
equinox. If the hour angle is measured with respect to the true
equinox, apparent sidereal time is being
measured. If the hour angle is measured with respect to the mean
equinox, mean sidereal time is being
measured. When the measurements are made with respect to the meridian
at Greenwich, the times are referred to as Greenwich mean sidereal
time (GMST) and Greenwich apparent sidereal time (GAST).
Given below is a simple algorithm for computing apparent sidereal
time to an accuracy of about 0.1 second, equivalent to about
1.5 arcseconds on the sky. The input time required by the algorithm is represented as a Julian date (
Julian dates can be used to determine
Universal Time.)
Let JD be the Julian date of the time of interest. Let
JD0 be the Julian date of the previous
midnight (0h) UT (the value of
JD0 will end in .5 exactly), and let H
be the hours of UT elapsed since that time. Thus we have
JD = JD0 + H/24.
For both of these Julian dates, compute the number of days and
fraction (+ or -) from 2000 January 1, 12h UT, Julian date 2451545.0:
D = JD - 2451545.0
D0 = JD0 - 2451545.0
Then the Greenwich mean sidereal time in hours is
GMST = 6.697374558 + 0.06570982441908 D0 + 1.00273790935 H + 0.000026 T2
where T = D/36525 is the number of centuries since the year 2000;
thus the last term can be omitted in most applications. It will be
necessary to reduce GMST to the range 0h to 24h.
Setting H = 0 in the above formula yields the Greenwich mean sidereal
time at 0h UT, which is tabulated in
The Astronomical Almanac.
The following alternative formula can be used with a loss of
precision of 0.1 second per century:
GMST = 18.697374558 + 24.06570982441908 D
where,
as above, GMST must be reduced to the range 0h to 24h.
The equations for GMST given above are adapted from those given in
Appendix A of USNO
Circular No. 163 (1981).
The Greenwich apparent sidereal time is obtained by adding a
correction to the Greenwich mean sidereal time computed above. The
correction term is called the nutation in right ascension or
the equation of the equinoxes. Thus,
GAST = GMST + eqeq.
The equation of the equinoxes
is given as eqeq = Δψ cos ε where Δψ, the nutation in longitude, is given
in hours approximately by
Δψ ≈ -0.000319 sin Ω - 0.000024 sin 2L
with Ω, the Longitude of the ascending node of the Moon, given as
Ω = 125.04 - 0.052954 D,
and L,
the Mean Longitude of the Sun, given as
L = 280.47 + 0.98565 D.
ε is the obliquity and is given as
ε = 23.4393 - 0.0000004 D.
The above expressions for Ω, L, and ε are all expressed
in degrees.
The mean or apparent sidereal time locally is found by
obtaining the local longitude in degrees, converting it to hours by
dividing by 15, and then adding it to or subtracting it from the
Greenwich time depending on whether the local position is east (add)
or west (subtract) of Greenwich.
If you need apparent sidereal time to better than 0.1 second
accuracy on a regular basis, consider using the Multiyear Interactive
Computer Almanac,
MICA.
MICA
provides very accurate almanac data in tabular form for a range of
years.
NOTES ON ACCURACY
The maximum error resulting from the use of the above formulas for
sidereal time over the period 2000-2100 is 0.432 seconds; the RMS
error is 0.01512 seconds. To obtain sub-second accuracy in
sidereal time, it is important to use the form of Universal Time
called UT1 as the basis for the input
Julian date.
The maximum value of the equation of the equinoxes is about 1.1
seconds, so if an error of ~1 second is unimportant, the last
series of formulas can be skipped entirely. In this case set
eqeq = 0 and GAST = GMST, and use either UT1 or UTC as the Universal Time basis for the
input Julian date.

Related

UTC to GPS time for finding TOW in Simulink

for my project, I need to calculate TOW (Time of week) in Simulink. I know this can be achieved through conversion of UTC time to GPS time.
I have written a simple m-file in Matlab which does the action for me in Matlab as follow:
date_gps_int = 10000*y + 100*m + d
date_gps_str = int2str(date_gps_int)
date_gps_str_to_serial = datenum(date_gps_str,'yyyymmdd')
date_str_format = datestr(date_gps_str_to_serial,'dd-mmmm-yyyy')
Num_Days = daysact('06-jan-1980',date_str_format)
Num_Weeks = Num_Days/7
TOW = Num_Weeks - 1024
My first intention was to use this as a function in simulink. But apparently because of 'datenum' and 'datestr' it is not possible, since simulink does not handle strings.
Now I am wondering if anyone can help me with this issue. Is there any way to calculate TOW from the UTC date in Matlab without using those predefined functions?
I also tried to write an algorithm for calculating number of days since '6 January 1980' and then calculating number of weeks by dividing that by 7. But since I am not very familiar with leap year calculation and I don't really know the formula for these kinds of calculations, my result differs from real TOW.
I would appreciate if anybody can help me on this.
There are three formats handled by Matlab for time: formatted date strings - what datestr outputs -, serial date - scalar double, what datenum outputs - and date vectors (see datevec). Conversion functions work with these three, and the most convenient way to convert individual variables (year, month, etc) to a date is to build a date vector [yyyy mm dd HH MM SS].
date_gps_str_to_serial = datenum([y m d 0 0 0]); % midnight on day y-m-d
date_Jan_6_1980 = datenum([1980 01 06 0 0 0]); % midnight on Jan 6th, 1980
Num_Days = date_gps_str_to_serial - date_Jan_6_1980;
Now, beware of leap seconds...
GPS time is computed form the time elapsed since Jan 6th 1980. Take the number of seconds elapsed since that day, as measured by the satellites' atomic clocks, divide by (24*3600) to get a number of days, the remainder is the time of the day (in seconds since midnight).
But, once in a while, the International Earth Rotation and Reference Systems Service will decide that a day will last one second longer to accommodate for the slowing of Earth rotation. It may happen twice a year, on June 30th or December 31st. The calculation of GPS time is wrong, because it does not take into account that some days last 86401 seconds (so dividing by 24*3600 does not work) and will advance by 1 second with respect to UTC each time this happens. There has been 18 such days since Jan 6th 1980, so one should subtract 18 seconds from GPS time to find UTC time. The next time a leap second may be added is June 2019.

How to get monthly totals from linearly interpolated data

I am working with a data set of 10,000s of variables which have been repeatedly measured since the 1980s. The first meassurements for each variable are not on the same date and the variables are irregularly measured - sometimes measurements are only a month apart, in a small number of cases they are decades apart.
I want to get the change in each variable per month.
So far I have a cell of dates of measurements,and interpolated rates of change between measurements (each cell represents a single variable in either, and I've only posted the first 5 cells in each array)
DateNumss= {[736614;736641;736669] [736636;736666] 736672 [736631;736659;736685] 736686}
LinearInterpss={[17.7777777777778;20.7142857142857;0] [0.200000000000000;0] 0 [2.57142857142857;2.80769230769231;0]}
How do I get monthly sums of the interpolated change in variable?
i.e.
If the first measurement for a variable is made on the January 1st, and the linearly interpolated change between that an the next measurement is 1 per day; and the next measurement is on Febuary the 5th and the corresponding linearly interpolated change is 2; then January has a total change of 1*31 (31 days at 1) and febuary has a total change of 1*5+2*23 (5 days at 1, 23 days at 2).
You would need the points in the serial dates that correspond with the change of a month.
mat(:,1)=sort(repmat(1980:1989,[1,12]));
mat(:,2)=repmat(1:12,[1,size(mat,1)/12]);
mat(:,3)=1;
monthseps=datenum(mat);
This gives you a list of all 120 changes of months in the eighties.
Now you want, for each month, the change per day, and sum it. If you take the original data it is easier, since you can just interpolate each day's value using matlab. If you only have the "LinearInterpss" you need to map it on the days using interp1 with the method 'previous'.
for ct = 2:length(monthseps)
days = monthseps(ct-1):(monthseps(ct)-1); %days in the month
%now we need each day assigned a certain change. This value depends on your "LinearInterpss". interp1 with method 'previous' searches LineairInterpss for the last value.
vals = interp1(DateNumss,LinearInterpss,days,'previous');
sum(vals); %the sum over the change in each day is the total change in a month
end

calculate monthly mean, 90th and 99th percentile of time series

I'm reading this article on wind speed trends and they specify in their methods that they tried to determine if there is a trend within the time series of monthly mean, 90th, and 99th percentile values of wind speed over the period shown. How would one achieve this? Furthermore, what does it mean by 90th and 99th percentile? My example:
v = datenum(1981, 1, 1):datenum(2010, 11, 31); % time vector
d = rand(1,length(v)); % data vector
% calculate mean, 90th and 99th percentile values
dateV = datevec(v); % date vector
[~,~,b] = unique(dateV(:,1:2),'rows');
monthly_v = accumarray(b,v,[],#mean);
monthly_d = accumarray(b,d,[],#mean);
I can calculate the monthly mean by the method shown above, but am not sure on how to calculate the 90th and 99th percentile (plus I'm not even sure what it is). Can anyone provide some information on this?
Use the prctile function. What you are seeking is a threshold where the proportion / percentage of input data that is exceeding this threshold is 100% - percentile. For example, if you sought the 90% quantile, you are trying to find a quantity in your input data where 10% of your data exceeded this quantity. For the 99% percentile, you are seeking the quantity in your input data where 1% of your data exceeded this threshold. You can simply call prctile by:
Y = prctile(X, P);
X is your data stored in vector form, and P is a vector or single number that lists the percentiles you desire. The output would be those thresholds that we just talked about, stored in Y.
In your case, v and d is your data you want to find the percentiles on per month, and thus you would modify your accumarray call like so:
monthly_v_90 = accumarray(b,v,[],#(x) prctile(x, 90));
monthly_v_99 = accumarray(b,v,[],#(x) prctile(x, 99));
monthly_d_90 = accumarray(b,d,[],#(x) prctile(x, 90));
monthly_d_99 = accumarray(b,d,[],#(x) prctile(x, 99));
What the above code will do is that for each unique month, you will calculate the 90% and 99% quantiles for v and d respectively. Specifically, monthly_v_90 and monthly_v_99 will give you the 90% and 99% quantiles for each month in a unique year for v while monthly_d_90 and monthly_d_99 will give you the 90% and 99% quantiles for each month in a unique year for d.
In your call to datevec, you are generating months from January 1981 to December 2010. Because there are 30 years in between, and there are 12 months in a year, you should have 360 element vectors with the above (as well as your calculations for the mean).

Iterate for loop by hour in MATLAB

I am writing a for loop to average 10 years of hourly measurements made on the hour. The dates of the measurements are recorded as MATLAB datenums.
I am trying to iterate through using 0.0417 as it is the datenum for 1AM 00/00/00 but it is adding in a couple of seconds of error each time I iterate.
Can anyone recommend a better way for me to iterate by hour?
date = a(:,1);
load = a(:,7);
%loop for each hour of the year
for i=0:0.0417:366
%set condition
%condition removes year from current date
c = date(:)-datenum(year(date(:)),0,0)==i;
%evaluate condition on load vector and find mean
X(i,2)=mean(load(c==1));
end
An hour has a duration of 1/24 day, not 0.0417. Use 1/24 and the precision is sufficient high for a year.
For an even higher precision, use something like datenum(y,1,1,1:24*365,0,0) to generate all timestamps.
To avoid error drift entirely, specify the index using integers, and divide the result down inside the loop:
for hour_index=1:365*24
hour_datenum = (hour_index - 1) / 24;
end

Generate a periodic value based on dates

I was hoping someone that is good with math and loops could help me out. I'm writing a program in Objective C where I need to come up with a way to do a cycle. If you don't know Objective C I would appreciate any help in pseudo code just to help me figure this out.
What I need is a scale that is based on two dates. I know this will be some sort of loop but not sure how to figure it out.
For instance, lets say that the first date is 5/25/1976 and the second date is 9/25/2009. Every 25 days there will be a "peak" so it's value will be 100. If I divide 23 in half I get 12 (rounded) so it would be the opposite or "valley" so it's numerical value would be 0. In other words on the 23rd day it would be at 100 but then on the 24th day it would start going back down and then bottom out 12 days later and then start the cycle back up and top out again at 23 days.
What I need to be able to do is find the numerical value for any given date in between any two given dates.
Thanks for any help you can offer!
value = 100*cos(2*pi*(numDays/25))
Or something like that.
Calculate the difference in days (optionally in fractional days too) between the starting point and the day you want the value for.
Divide by the cycle period (could be 23 or 25 according to the question).
Take the fractional part.
Apply the correct periodic function - for example, either sin() or cos(), appropriately scaled for the trigonometric functions (multiply the fraction by 2π).
You could simulate the shape by values out of a table describing the values indexed on days into the period (so you would use waveform[Δt mod period] to determine the value).
The NSDate class has a method timeIntervalSinceDate that will give you then number of seconds between two dates. You could calculate the number of days between two dates like this:
- (double) daysBetweenStart:(NSDate*)start end:(NSDate*)end
{
return [start timeIntervalSinceDate:end] / 86400.0; // seconds in a day
}
You could use this to compute a step function based on that:
- (double) someDescriptiveFunctionName:(NSDate*)date fromDate:(NSDate*)start
{
double days = [self daysBetweenStart:start end:date];
if ((int) days % 23 == 0)
return 100.0;
else
return 0.0;
}
This function returns 100.0 if the given date is between 23 and 24 days from the start, and 0.0 otherwise. You could substitute 23 for whatever period you like. I'm not sure if this is what you wanted, so clarify your question if it wasn't.
Disclaimer: This is Cocoa. Hopefully it's the same as iPhone Cocoa?