Difference between two dates ignoring year - python

Python datetime objects require a year, however I would just like to write a function that outputs the differences in dates between two day-months ignoring year. Meaning the output range of this function is [-182, 183], since the calculations should "wrap around" the year.
Example:
Goal date: 1st Jan
Guess: 31st Dec
Result: -1
Goal date: 1st Jan
Guess: 2nd Jan
Result: +1
def date_diff(goal, guess):
#goal and guess are in the format "%m-%d"
goal_date = datetime.strptime(goal + "-2020", "%m-%d-%Y")
goal_date1 = datetime.strptime(goal + "-2021", "%m-%d-%Y")
guess_date = datetime.strptime(guess + "-2020", "%m-%d-%Y")
guess_date1 = datetime.strptime(guess + "-2021", "%m-%d-%Y")
r1 = goal_date - guess_date
r1 = r1.days
r3 = goal_date1 - guess_date
r3 = r3.days
r2 = guess_date1 - goal_date
r2 = r2.days
r4 = guess_date - goal_date
r4 = r4.days
r = ((r1, math.copysign(1, r1)), (r2, math.copysign(1, r2)),(r3, math.copysign(1, r2)),(r4, math.copysign(1, r4)))
#idea here was the find min of index 0 of each tuple then restore the sign, but i think i'm missing some combinations
smallest = min(r, key = lambda x:abs(x[0]))
return smallest[0]*smallest[1]

You can take the minimum (absolute) difference with the second date at the previous, same and next year of the first date
For example :
from datetime import date
def dateDelta(d1,d2):
return min(((date(d1.year+i,d2.month,d2.day)-d1).days
for i in (-1,0,1)),key=abs)
d1 = date(2002,10,15)
d2 = date(2002,2,3)
print(dateDelta(d1,d2)) # 111
print(dateDelta(d2,d1)) # -111
Note that you may get varying results depending on the reference year you chose. To avoid interference from leap years, select a non-leap year for d1 that is neither before nor after a leap year (e.g. 2002, or any year mod 4 == 2)

Related

How can I assign a list that I created to a dataframe cell?

I have a dataframe, dfTest, with three columns. One has year information, one has month information, and one is a list of dates.
Date1_Year Date1_Month Datelist
2020 5 ['2020-04','2020-01']
2020 9 [‘2020-08’, ‘2020-07’,’2020-06’]
2021 9 ['2021-09']
2020 12 ['2020-10','2020-01']
1999 03 NaN
Taking the first row as an example, I would like to calculate the number of months between the date represented by Date1_Year and Date2_Year (that is '2020-05') and each of the dates in DateList. The desired output would be:
Date1_Year Date1_Month DateList Month_Diff
2020 5 ['2020-04','2020-01'] [1,4]
2020 9 ['2020-08','2020-07','2020-06'] [1,2,3]
2021 9 ['2021-09'] [0]
2020 12 ['2020-10','2020-01'] [2,11]
1999 03 NaN
This is my code:
for i in range(len(dfTest)):
My_Date_List = dfTest.loc[i,'DateList']
Date1Months = dfTest.loc[i,'Date1_Year']*12 + dfTest.loc[i,'Date1_Month']
months_since_date1 = []
if isinstance(My_Date_List,list):
if len(My_Date_List) >= 1:
for j in range(len(My_Date_List)):
Date2_Year = int(My_Date_List[j][:4])
Date2_Month = int(My_Date_list[j][5:])
Date2Months = Date2_Year*12 + Date2_Month
if Date1Months < Date2Months:
months_since_date1.append(-999)
else:
months_since_date1.append(Date1Months-Date2Months)
dfTest.loc[i,'Month_Diff'] = months_since_date1
I was able to test that months_since_date1 was being created as I expected, but when I add the line of code
dfTest.loc[i,'Month_Diff'] = months_since_date1
I get the message "ValueError: must have equal len keys and value when setting an iterable"
Please advise! What am I doing wrong?
Previous initialization via dfTest['Month_Diff'] = [[]] * len(dfTest) and later assignment via dfTest['Month_Diff'][i] = months_since_date1 do the trick, resulting in
#initialize the new column
dfTest['Month_Diff'] = [[]] * len(dfTest)
for i in range(len(dfTest)):
My_Date_List = dfTest.loc[i,'DateList']
Date1Months = dfTest.loc[i,'Date1_Year']*12 + dfTest.loc[i,'Date1_Month']
months_since_date1 = []
if isinstance(My_Date_List,list):
if len(My_Date_List) >= 1:
for j in range(len(My_Date_List)):
Date2_Year = int(My_Date_List[j][:4])
Date2_Month = int(My_Date_List[j][5:])
Date2Months = Date2_Year*12 + Date2_Month
if Date1Months < Date2Months:
months_since_date1.append(-999)
else:
months_since_date1.append(Date1Months-Date2Months)
#assign the resulting list
dfTest['Month_Diff'][i] = months_since_date1

Select dates in index

i have start date and end date and dataframe with daily observations. The problem is that i can't find a way, which will enable me select dates with periodicity of 3 months
for example:
2003-01-03 + 3 months = 2003-04-03 and so on
output should consist of 20 rows because 5 years with 3 months periodicity, including start and end dates
EDIT: Old solution didn't work for all cases. Therefore a new one:
start, end = returns.index[0], returns.index[-1]
length = (end.year - start.year) * 12 + (end.month - start.month)
if length % 3 == 0 and end.day >= start.day:
length += 3
new_index = []
for m in range(3, length, 3):
ydelta, month = divmod(start.month + m, 12)
day = pd.Timestamp(year=start.year + ydelta, month=month, day=1)
day += pd.Timedelta(f'{min(start.day, day.days_in_month) - 1}d')
new_index.append(day)
new_index = pd.DatetimeIndex(new_index)
returns = returns.loc[new_index]
Another version which has some slight inaccuracies around the month ends but is more compact:
add_3_months = pd.tseries.offsets.DateOffset(months=3)
new_index = pd.date_range(returns.index[0] + add_3_months,
returns.index[-1],
freq=add_3_months)
returns = returns.loc[new_index]

How to solve the weekday function problem Python

I am very new to Python and we were told to write the weekday function without any modules like e.g. daytime etc.
But it doesn't work and i am not sure where is a problem
def weekDay (day,month,year):
global y0
global m0
global x
y0 = 0
m0 = 0
day,month,year = int(input("Enter a day: "))
month = int(input("Enter a month: "))
year = int(input("Enter a year:"))
a = (day + x + (31 * m0) // 12) % 7
for m0 in a:
m0 = month + 12 * ((14 - month) // 12) - 2
for x in a:
x = y0 + y0 // 4 - y0 // 100 + y0 // 400
for y0 in x:
y0 = year - ((14 - month) // 12)
if a == 0:
print("Sunday")
elif a == 1:
print("Monday")
elif a == 2:
print("Tuesday")
elif a == 3:
print("Wednesday")
elif a == 4:
print("Thursday")
elif a == 5:
print("Friday")
else:
print("Error")
return weekDay(a)
'''
here is the formula we were given:
[![formula][1]][1]
[1]: https://i.stack.imgur.com/iBv30.png
This should help:
>>> import datetime
>>> now = datetime.datetime.now()
>>> now.strftime('%A')
'Friday'
>>>
Global variables not defined anywhere and I am not able to understand the logic you are trying to write. So written a function based on a aptitude trick.
def weekday(day,month,year):
"""
This function is written based upon aptitude trick
to obtain day from given a date.
Input date example : 15-5-2020
Link for logic : https://www.youtube.com/watch?v=rJ0_GWDTdD4
"""
# for different months we are assigning specific number to that month
months = {1:1, 2:4, 3:4, 4:0, 5:2, 6:5, 7:0, 8:3, 9:6, 10:1, 11:4, 12:6}
# assigning days to a number
day_name = {1:'Sunday', 2:'Monday', 3:'Tuesday', 4:'Wednesday', 5:'Thursday',
6:'Friday', 0:'Saturday'}
# to get the year in between 1600 and 2000. since we are assiging values
# for the years also
while year not in range(1600,2000):
if year>2000:
year-=400
if year<1600:
year+=400
# assigning values to years
if year in range(1600,1700):
yr = 6
if year in range(1700,1800):
yr = 4
if year in range(1800,1900):
yr = 2
if year in range(1900,2000):
yr = 0
# assigning last two numbers of year to last
first = year//100
last = year - (first * 100)
# obtaining remainder
res = (day + months[month] + yr + last + (last//4))%7
#returning the day_name
return day_name[res]
day,month,year = list(map(int,input("Enter date in format dd-mm-yyyy : ").split('-')))
print(weekday(day,month,year))
Hope, you are satisfied with logic.

Making a time adding function in python

I'm trying to build a function that recieves a date and adds days, updating everything in case it changes, so far i've come up with this:
def addnewDate(date, numberOfDays):
date = date.split(":")
day = int(date[0])
month = int(date[1])
year = int(date[2])
new_days = 0
l = 0
l1 = 28
l2 = 30
l3 = 31
#l's are the accordingly days of the month
while numberOfDays > l:
numberOfDays = numberOfDays - l
if month != 12:
month += 1
else:
month = 1
year += 1
if month in [1, 3, 5, 7, 8, 10, 12]:
l = l3
elif month in [4, 6, 9, 11]:
l = l2
else:
l = l1
return str(day) + ':' + str(month) + ':' + str(year) #i'll deal
#with fact that it doesn't put the 0's in the < 10 digits later
Desired output:
addnewDate('29:12:2016', 5):
'03:01:2017'
I think the problem is with either the variables, or the position i'm using them in, kinda lost though..
Thanks in advance!
p.s I can't use python build in functions :)
Since you cannot use standard library, here's my attempt. I hope I did not forget anything.
define a table for month lengths
tweak it if leap year detected (every 4 year, but special cases)
work on zero-indexed days & months, much easier
add the number of days. If lesser that current month number of days, end, else, substract current month number of days and retry (while loop)
when last month reached, increase year
add 1 to day and month in the end
code:
def addnewDate(date, numberOfDays):
month_days = [31,28,31,30,31,30,31,31,30,31,30,31]
date = date.split(":")
day = int(date[0])-1
month = int(date[1])-1
year = int(date[2])
if year%4==0 and year%400!=0:
month_days[1]+=1
new_days = 0
#l's are the accordingly days of the month
day += numberOfDays
nb_days_month = month_days[month]
done = False # since you don't want to use break, let's create a flag
while not done:
nb_days_month = month_days[month]
if day < nb_days_month:
done = True
else:
day -= nb_days_month
month += 1
if month==12:
year += 1
month = 0
return "{:02}:{:02}:{:04}".format(day+1,month+1,year)
test (may be not exhaustive):
for i in ("28:02:2000","28:02:2004","28:02:2005","31:12:2012","03:02:2015"):
print(addnewDate(i,2))
print(addnewDate(i,31))
result:
02:03:2000
31:03:2000
01:03:2004
30:03:2004
02:03:2005
31:03:2005
02:01:2013
31:01:2013
05:02:2015
06:03:2015
of course, this is just for fun. Else use time or datetime modules!

How to generate random time series data including February with 29 days?

I have used the following codes to generate a random rainfall data from 1950 to 2009 with known probability, mean and standard deviations. But, I have been suffering from iterating the days of February as 29 days in the leap years. And, also I was trying to save the output in a text file, but it gives an error message like
TypeError: float argument required, not numpy.string_
Can anyone please help me out?
My code:
import numpy as np
import random
import itertools
import datetime
dry =[0.33,0.27,0.32,0.41,0.42,0.45,0.57,0.52,0.45,0.39,0.37,0.37]
wet = [0.66,0.72,0.67,0.58,0.57,0.54,0.42,0.47,0.54,0.60,0.62,0.62]
d2d_tran = [0.56,0.50,0.58,0.62,0.63,0.67,0.73,0.66,0.60,0.56,0.57,0.62]
w2w_tran = [0.78,0.80,0.79,0.73,0.72,0.72,0.63,0.64,0.66,0.71,0.74,0.76]
mu = [3.71, 4.46, 4.11, 2.94, 3.00, 2.87, 2.31, 2.44, 2.56, 3.45, 4.32, 4.12]
sigma = [6.72,7.92,7.49,6.57,6.09,5.53,4.38,4.69,4.31,5.71,7.64,7.54]
days = [31,28,31,30,31,30,31,31,30,31,30,31]
rain = []
for y in xrange(0,60):
for m in xrange(0,12):
random_num = np.random.rand(days[m])
if random.random() <= dry[m]:
random_num[0] = 0
else:
r = abs(random.gauss(mu[m],sigma[m]))
random_num[0] = r
for i in xrange(1,days[m]):
if random_num[i-1] == 0:
if random_num[i] <= d2d_tran[m]:
random_num[i] = 0
else:
r = abs(random.gauss(mu[m],sigma[m]))
random_num[i] = r
else:
if random_num[i] <= w2w_tran[m]:
r = abs(random.gauss(mu[m],sigma[m]))
random_num[i] = r
else:
random_num[i] = 0
rain.append(random_num)
rain_series = []
for j in itertools.chain.from_iterable(rain):
rain_series.append(j)
y = np.array(rain_series).reshape(-1, 1)
date_series = []
def generate_dates(start_date, end_date):
return (start_date + datetime.timedelta(days=d) for d in xrange((end_date - start_date).days + 1))
start_date = datetime.date(1950, 1, 1)
end_date = datetime.date(2009, 12, 16)
for current_date in generate_dates(start_date, end_date):
f = current_date.strftime('%Y-%m-%d')
date_series.append(f)
z = np.array(date_series).reshape(-1, 1)
#### Here I have 365x60 = 21900 rainfall values, that is why I had to
####set the end_date upto (2009,12,16). If
#### the February days for leap years can be set as 29 in days[] of
####rain_series than this problem would be solved.
data = np.concatenate((z,y), axis=1)
print data
data1 = data.reshape((21900,2))
np.savetxt('Random Rainfall Data.txt', data1)
#### I want to shape data in two columns like dates and rainfall.
#### And than, save it into a text file. But, it provides an error!!!
Use the calendar.monthrange() to get the number of days of a month.
for year in xrange(1950,2020):
for month in xrange(1,13):
day_num = calendar.monthrange(year, month)[1]
random_num = np.random.rand(day_num)
...
Regarding the data writing problem, you must add a third argument. It depends on your specific problem:
np.savetxt('test.txt', a, '%s') # Example
See documentation for more formatting info.

Categories

Resources