netcdf - CDO monmean - netcdf

I have a netcdf file, with a daily time step, that I wish to convert to a monthly time step.
The time is formatted as follow:
double time(time) ;
time:standard_name = "time" ;
time:long_name = "time" ;
time:bounds = "time_bnds" ;
time:units = "days since 2000-01-01" ;
time:calendar = "standard" ;
time:axis = "T" ;
When I convert to monthly time step using the command:
cdo monmean input.nc output.nc
Everything works fine except that the time output is strange:
time = "2000-01-16", "2000-02-15", "2000-03-16", "2000-04-15 12",
"2000-05-16", "2000-06-15 12", "2000-07-16", "2000-08-16",
"2000-09-15 12", "2000-10-16", "2000-11-15 12", "2000-12-16";
I wish to replace the day on the monthly value by the first day of the month and also remove those odd 12's for the time that appear. The desired output:
time = "2000-01-01", "2000-02-01", "2000-03-01", "2000-04-01",
"2000-05-01", "2000-06-01", "2000-07-01", "2000-08-01",
"2000-09-01", "2000-10-01", "2000-11-01", "2000-12-01";
Any hints is appreciate

cdo --timestat_date first monmean input.nc output.nc
works for me, I hope it's helpful! It places the timestamp at the first step of the averaging period, whereas the default is in the middle. (There is also a --timestat_date last if one want to do the opposite and put it at the last step of the window)

Related

How to make netcdf time format as YYYY-MM-DD HH:MM:SS from YYYY-MM-DD HH?

My netcdf4 file has the following header for the time variable
float time(time) ;
time:standard_name = "time" ;
time:long_name = "time" ;
time:units = "hours since 1900-01-01 00:00:00.0" ;
time:calendar = "gregorian" ;
time:axis = "T" ;
With the following command
ncdump -tv time infile.nc
time format looks like
"2009-12-31 22"
I want to make it so that it is like
"2020-12-31 22:00"
How do I achieve this with either nco or cdo??
If the file is not huge you could try
cdo -outputtab,date,time infile.nc | sort | uniq
I think it also assumes that time is the unlimited dimension, and may produce errors otherwise.

Groovy: Time in ISO 8601 format

How to get the current time and 15 min ago time in iso 8601 format (YYYY-MM-DDTHH:mm:ss) in groovy?
You can use java time's Instant and the toString() format
import java.time.*
def now = Instant.now()
def fifteenAgo = now.minus(Duration.ofMinutes(15))
println "Now is ${now} and 15 mins ago was ${fifteenAgo}"
Prints:
Now is 2020-06-30T19:53:17.445039Z and 15 mins ago was 2020-06-30T19:38:17.445039Z
You can formast the date in any way you want in Groovy, by doing e.g.
println new Date().format("yyyy-MM-dd HH.mm.ss.SSSSS Z")
Then, you can do calculations on the date, like this:
new Date(System.currentTimeMillis()-91*60*1000)
which will minus 91 minutes (91min * 60sec * 1000ms).
Then you can put the statements together, which is why Groovy is great:
def a = new Date(System.currentTimeMillis()-91*60*1000).format("YYYY-MM-DD")
And so you can get the half before the T. And the half after the T:
def b = new Date(System.currentTimeMillis()-91*60*1000).format("HH:mm:ss")
And then concatenate them with a T:
println "91 minutes ago in iso 8601 format is: ${a}T${b}"
There are other ways of doing it, like with TimeCategory.minus, but this is a good illustration. I used 91 minutes, but you can adapt it to your own requirtement.

Count the amount of observations in predetermined timestep

I have a large dataset of over 75.000 observations. Of these observations I have a list of date and time combinations. I want to calculate the observation frequency in a predetermined timestep (15, 30 or 60 minutes). The study period is from 2014-10-21 00:00 to 2015-10-21 23:59.
The raw data is stored in a DF, but date (as POSIXlt) and time (as character) are in different columns, so I combine them back into one column to create a POSIXct timestamp.
receiver$date2 = as.POSIXct(paste(receiver$date, receiver$time), format="%Y-
%m-%d %H:%M:%S")
dateseq = receiver$date2
dateseq is now (only a small fragment using dput()):
dateseq = structure(c(1414140420, 1414140720, 1414140960, 1414141080, 1414143540, 1414144980, 1414145940, 1414147380, 1414147440, 1414148100, 1414148280, 1414152720, 1414153740, 1414154520, 1414154580, 1414158540, 1414159380, 1414159680, 1414164240, 1414164300, 1414164840, 1414164900, 1414165500, 1414166100, 1414166220, 1414166460, 1414166520, 1414166820, 1414166880, 1414166940, 1414167300, 1414167360, 1414167480, 1414167780, 1414168380, 1414168440, 1414168800, 1414168860, 1414202040, 1414202220, 1414202280, 1414202700, 1414202820, 1414202880, 1414203660, 1414203960, 1414215180, 1414215300, 1414215900, 1414216560, 1414216860, 1414217220, 1414217280, 1414217460, 1414217580, 1414217700, 1414217820, 1414217880, 1414218240, 1414218720, 1414219380, 1414219800, 1414219920, 1414219980, 1414220160, 1414220280, 1414220820, 1414220880, 1414221000, 1414221960, 1414222080, 1414222200, 1414222320, 1414222500, 1414222560, 1414222860, 1414223640, 1414224780, 1414225800, 1414225920, 1414225980, 1414226040, 1414226100, 1414226220, 1414227240, 1414227420, 1414227600, 1414230300, 1414230540, 1414230840, 1414231140, 1414231320, 1414231440, 1414231560, 1414231800, 1414231860, 1414232040, 1414232160, 1414232400, 1414232520, 1414232640, 1414232700, 1414232760, 1414232880, 1414232940, 1414233060, 1414233180, 1414233240, 1414233300, 1414233420, 1414233480, 1414233660, 1414233720, 1414233780, 1414233840, 1414233960, 1414234080, 1414234320, 1414234440, 1414234560, 1414234620, 1414234740, 1414234860, 1414234980, 1414235040, 1414235280, 1414236240, 1414236300, 1414236420, 1414236540, 1414236840, 1414236900, 1414236960, 1414237020, 1414237260, 1414237560, 1414237860, 1414238280, 1414238400, 1414238460, 1414238580, 1414238640, 1414239180, 1414239300, 1414239360, 1414239480, 1414239540, 1414240440, 1414240860, 1414240920, 1414240980, 1414241040, 1414242000, 1414242180, 1414242480, 1414242540, 1414242660, 1414242720, 1414242840, 1414242900, 1414243800, 1414243920, 1414244280, 1414244460, 1414245240, 1414245600, 1414245660, 1414246080, 1414246500, 1414246680, 1414246740, 1414246920, 1414247340, 1414248180, 1414249320, 1414249560, 1414249860, 1414250340, 1414250520, 1414250640, 1414250760, 1414250880, 1414250940, 1414251060, 1414251240, 1414251900, 1414252020, 1414252080, 1414252200, 1414252260, 1414252380, 1414252440, 1414252440, 1414252500, 1414252560, 1414252680, 1414252980, 1414253160, 1414253460, 1414253580), class = c("POSIXct", "POSIXt"), tzone = "")
Then I want to have a timeseq that runs for the whole period (so also the days that don't have any observations) divided by the predetermined timestep.
timestep = 1800 # 1800 sec = 30 min
start = "2014-10-21 00:00"
end = "2015-10-21 23:59"
receiver = R125926
timeseq = seq(from = as.POSIXct(start), to = as.POSIXct(end), by = timestep)
Now I want to 'fill' a new dataframe with the timeseq in one column and the count data of how many observations (from dateseq) occurred in that time period.
EDIT
After some searching on the forum and adjusting some code, I came to one very simple method that brings me very close to what I want my results to look like:
det_interval = data.frame(table(cut(dateseq, breaks = "30 min")))
There's only two adjustments that I don't know how to do. Now it begins at the first record (e.g. when my first record is on 05.17 the interval it will use will be x.17 - x.47 (30min)), not at the start that I want (see the timeseq created above). So how can I make sure that this starts and ends at a predetermined date/time?

File renaming based on file content in UNIX

I have pattern namely QUARTERDATE and FILENAME inside the file.
Both will have some value as in below eg.
My requirement is, I should rename the file like FILENAME_QUARTERDATE.
My file(myfile.txt) will be as below:
QUARTERDATE: 03/31/14 - 06/29/14
FILENAME : LEAD
field1 field2
34567
20.0 5,678
20.0 5,678
20.0 5,678
20.0 5,678
20.0 5,678
I want the the file name to be as LEAD_201402.txt
Date range in the file is for Quarter 2, so i given as 201402.
Thanks in advance for the replies.
newname=$(awk '/QUARTERDATE/ { split($4, d, "/");
quarter=sprintf("%04d%02d", 2000+d[3], int((d[1]-1)/3)+1); }
/FILENAME/ { fn = $3; print fn "_" quarter; exit; }' "$file")
mv "$file" "$newname"
How is a quarter defined?
As noted in comments to the main question, the problem is as yet ill-defined.
What data would appear in the previous quarter's QUARTERDATE line? Could Q1 ever start with a date in December of the previous year? Could the end date of Q2 ever be in July (or Q1 in April, or Q3 in October, or Q4 in January)? Since the first date of Q2 is in March, these alternatives need to be understood. Could a quarter ever start early and end late simultaneously (a 14 week quarter)?
To which the response was:
QUARTERDATE of Q2 will start as 1st Monday of April and end as last Sunday of June.
Which triggered a counter-response:
2014-03-31 is a Monday, but hardly a Monday in April. What this mainly means is that your definition of a quarter is, as yet, not clear. For example, next year, 2015-03-30 is a Monday, but 'the first Monday in April' is 2015-04-06. The last Sunday in March 2015 is 2015-03-29. So which quarter does the week (Mon) 2015-03-30 to (Sun) 2015-04-05 belong to, and why? If you don't know (both how and why), we can't help you reliably.
Plausible working hypothesis
The lessons of Y2K have been forgotten already (why else are two digits used for the year, dammit!).
Quarters run for an integral number of weeks.
Quarters start on a Monday and end on a Sunday.
Quarters remain aligned with the calendar quarters, rather than drifting around the year. (There are 13 weeks in 91 days, and 4 such quarters in a year, but there's a single extra day in an ordinary year and two extra in a leap year, which mean that occasionally you will get a 14-week quarter, to ensure things stay aligned.)
The date for the first date in a quarter will be near 1st January, 1st April, 1st July or 1st October, but the month might be December, March (as in the question), June or September.
The date for the last date in a quarter will be near 31st March, 30th June, 30th September, 31st December, but the month might be April, July, October or January.
By adding 1 modulo 12 (values in the range 1..12, not 0..11) to the start month, you should end up with a month firmly in the calendar quarter.
By subtracting 1 modulo 12 (values in the range 1..12 again) to the end month, you should end up with a month firmly in calendar quarter.
If the data is valid, the 'start + 1' and 'end - 1' months should be in the same quarter.
The early year might be off-by-one if the start date is in December (but that indicates Q1 of the next year).
The end year might be off-by-one if the end date is in January (but that indicates Q4 of the prior year).
More resilient code
Despite the description above, it is possible to write code that detects the quarter despite any or all of the idiosyncrasies of the quarter start and end dates. This code borrows a little from Barmar's answer, but the algorithm is more resilient to the vagaries of the calendar and the quarter start and end dates.
#!/bin/sh
awk '/QUARTERDATE/ {
split($2, b, "/")
split($4, e, "/")
if (b[1] == 12) { q = 1; y = e[3] }
else if (e[1] == 1) { q = 4; y = b[3] }
else
{
if (b[3] != e[3]) {
print "Year mismatch (" $2 " vs " $4 ") in file " FILENAME
exit 1
}
m = int((b[1] + e[1]) / 2)
q = int((m - 1) / 3) + 1
y = e[3]
}
quarter = sprintf("%.4d%.2d", y + 2000, q)
}
/FILENAME/ {
print $3 "_" quarter
# exit
}' "$#"
The calculation for m adds the start month plus one to the end month minus one and then does integer division by two. With the extreme cases already taken care of, this always yields a month number that is in the correct quarter.
The comment in front of the exit associated with FILENAME allows testing more easily. When processing each file separately, as in Barmar's example, that exit is an important optimization. Note that the error message gives an empty file name if the input comes from standard input. (Offhand, I'm not sure how to print the error message to standard error rather than standard output, other than by a platform-specific technique such as print "message" > "/dev/stderr" or print "message" > "/dev/fd/2".)
Given this sample input data (semi-plausible start and end dates for 6 quarters from 2014Q1 through 2015Q2):
QUARTERDATE: 12/30/13 - 03/30/14
FILENAME : LEAD
QUARTERDATE: 03/31/14 - 06/29/14
FILENAME : LEAD
QUARTERDATE: 06/30/14 - 09/28/14
FILENAME : LEAD
QUARTERDATE: 09/29/14 - 12/28/14
FILENAME : LEAD
QUARTERDATE: 12/29/14 - 03/29/15
FILENAME : LEAD
QUARTERDATE: 03/30/15 - 06/29/15
FILENAME : LEAD
The output from this script is:
LEAD_201401
LEAD_201402
LEAD_201403
LEAD_201404
LEAD_201501
LEAD_201502
You can juggle the start and end dates of the quarters within reason and you should still get the required output. But always be wary of calendrical calculations; they are almost invariably harder than you expect.

Python convert military time user input and calculate time worked (datetime.timedelta)

Noob here,
I'm stuck at trying to present user input in military time into standard time. The code works so far, but I need to subtract 12 hours from the end time to display in standard time. How do I do this using datetime.time? Also, do I need to convert the original user input to an integer to perform datetime.timedelta calculations? Previous questions don't seem to answer my coding questions.
My code is:
def timeconvert():
print "Hello and welcome to Python Payroll 1.0."
print ""
# User input for start time. Variable stored.
start = raw_input("Enter your check-in time in military format (0900): ")
# User input for end time. Variable stored.
end = raw_input("Enter your check-out time in military format (1700): ")
print ""
# ---------------------------------------------------------------------------
# Present user input in standard time format hhmm = hh:mm
# ---------------------------------------------------------------------------
import datetime, time
convert_start = datetime.time(hour=int(start[0:2]), minute=int(start[2:4]))
# need to find a way to subtract 12 from the hour to present end time in standard time
convert_end = datetime.time(hour=int(end[0:2]), minute=int(end[2:4]))
print 'You started at', convert_start.strftime("%H:%M"),'am', 'and ended at', convert_end.strftime("%H:%M"), 'pm'
# ---------------------------------------------------------------------------
# Use timedelta to caculate time worked.
# ---------------------------------------------------------------------------
# print datetime.timedelta
timeconvert()
raw_input("Press ENTER to exit program") # Closes program.
Thanks.
You can use strftime("%I:%M %p") to get standard 12 hour formatting with "AM" or "PM" at the end. See the Python documentation for more details on datetime string formatting.
Also, while it is not natively supported, you can simply use the two datetime.time instances to do your calculation as part of the timedelata constructor.
The below code should suffice, though proper error checking should definitely be used. ;)
--ap
start = raw_input("Enter your check-in time in military format (0900): ")
end = raw_input("Enter your check-out time in military format (1700): ")
# convert user input to datetime instances
start_t = datetime.time(hour=int(start[0:2]), minute=int(start[2:4]))
end_t = datetime.time(hour=int(end[0:2]), minute=int(end[2:4]))
delta_t = datetime.timedelta(
hours = (end_t.hour - start_t.hour),
minutes = (end_t.minute - start_t.minute)
)
# datetime format
fmt = "%I:%M %p"
print 'You started at %s and ended at %s' % (start_t.strftime(fmt), end_t.strftime(fmt))
print 'You worked for %s' % (delta_t)
def time12hr(string):
hours = string[:2]
minutes = string[2:]
x = " "
if int(hours) == 12:
x = "p.m."
hours = "12"
elif int(hours) == 00:
x = "a.m."
hours = "12"
elif int(hours) > 12:
x = "p.m."
hours = str(int(hours) - 12)
else:
x = "a.m."
return "%s:%s %s"%(hours ,minutes,x)
print time12hr('1202')
print time12hr('1200')
print time12hr('0059')
print time12hr('1301')
print time12hr('0000')

Resources