我正在寻找一种将datetime对象转换为十进制年的方法。以下是一个例子
>>> obj = SomeObjet()
>>> obj.DATE_OBS
datetime.datetime(2007, 4, 14, 11, 42, 50)
如何将datetime.datetime(2007,4,14,11,42,50)转换为十进制年份?从这种格式dd / mm / yyyy到这种格式yyyy.yyyy
答案 0 :(得分:21)
from datetime import datetime as dt
import time
def toYearFraction(date):
def sinceEpoch(date): # returns seconds since epoch
return time.mktime(date.timetuple())
s = sinceEpoch
year = date.year
startOfThisYear = dt(year=year, month=1, day=1)
startOfNextYear = dt(year=year+1, month=1, day=1)
yearElapsed = s(date) - s(startOfThisYear)
yearDuration = s(startOfNextYear) - s(startOfThisYear)
fraction = yearElapsed/yearDuration
return date.year + fraction
演示:
>>> toYearFraction(dt.today())
2011.47447514
这种方法可能精确到秒(或如果夏令时或其他奇怪的区域事物有效)。它在leapyears期间也能正常工作。如果您需要极高的分辨率(例如由于地球自转的变化),您最好不要查询网络服务。
答案 1 :(得分:2)
我假设您使用它来比较日期时间值。为此,请使用timedelta对象,而不是重新启动方向盘。
示例:
>>> from datetime import timedelta
>>> from datetime import datetime as dt
>>> d = dt.now()
>>> year = timedelta(days=365)
>>> tomorrow = d + timedelta(days=1)
>>> tomorrow + year > d + year
True
如果由于某种原因你确实需要十进制年,datetime
对象方法strftime()
可以给出一年中某一天的整数表示如果要求%j
- 如果这是您正在寻找的,请参阅下面的简单样本(仅限1天的分辨率):
>>> from datetime import datetime
>>> d = datetime(2007, 4, 14, 11, 42, 50)
>>> (float(d.strftime("%j"))-1) / 366 + float(d.strftime("%Y"))
2007.2814207650274
答案 2 :(得分:1)
在实施接受的解决方案后,我得知这个现代熊猫版本是相同的,更简单:
dat['decimal_date']=dat.index.year+ (dat.index.dayofyear -1)/365
必须在日期时间索引Pandas数据帧上使用。添加为此解决方案的帖子出现在谷歌搜索此问题的顶部。
答案 3 :(得分:1)
简答
日期到十进制年份转换的定义含糊不清,超出了 0.002 年(~1 天)的精度。对于小数精度不重要的情况,这将起作用:
# No library needed, one-liner that's probably good enough
def decyear4(year, month, day, h=0, m=0, s=0) :
return year + ((30.4375*(month-1) + day-1)*24+h)*3600/31557600.0
如果您需要精度优于 0.005 年(约 2 天),您应该使用其他东西(例如,自纪元以来的秒数,或类似的)。如果您被迫(或只是真的、真的想这样做)使用十进制年份,请继续阅读。
长答案
与之前发布的一些答案和评论相反,“小数年”日期/时间戳不是一个明确定义的数量。当您考虑十进制年份的概念时,您可能希望有两个属性为真:
年初和年末的完美插值:
2020 年 1 月 1 日上午 12:00:00 对应 2020.000
2020, Dec 31 11:59:59.999... pm 将对应于 2020.999...
常数单位(即线性映射):
2020.03-2020.02 == 2021.03-2021.02
不幸的是,您不能同时满足这两个条件,因为闰年和非闰年的 1 年时间长度不同。第一个要求是大多数以前的答案都试图满足的要求。但在许多(大多数?)情况下,实际上可能会使用十进制年份(例如,它将用于某种回归或模型中),那么第二个属性同样(如果不是更重要)同样重要。
这里有一些选项。我为 numpy 以矢量化形式做了这些,所以如果不需要 numpy,其中一些可以简化一些。
import numpy as np
# Datetime based
# Non-linear time mapping! (Bad for regressions, models, etc.
# e.g. 2020.2-2020.1 != 2021.2-2021.1)
def decyear1(year, month, day, h=0, m=0, s=0) :
import datetime
year_seconds = (datetime.datetime(year,12,31,23,59,59,999999)-datetime.datetime(year,1,1,0,0,0)).total_seconds()
second_of_year = (datetime.datetime(year,month,day,h,m,s) - datetime.datetime(year,1,1,0,0,0)).total_seconds()
return year + second_of_year / year_seconds
# Basically the same as decyear1 but without datetime library
def decyear2(year, month, day, h=0, m=0, s=0) :
leapyr = ((np.r_[year]%4==0) * (np.r_[year]%100!=0) + (np.r_[year]%400==0)).astype(int)
day_of_year = np.r_[0,31,28,31,30,31,30,31,31,30,31,30,31].cumsum()
year_seconds = ( (day_of_year[-1]+leapyr )*24*3600)
extraday = np.r_[month>2].astype(int)*leapyr
second_of_year = (((( day_of_year[month-1]+extraday + day-1)*24 + h)*60+m)*60+s)
return year + second_of_year / year_seconds
# No library needed
# Linear mapping, some deviation from some conceptual expectations
# e.g. 2019.0000 != exactly midnight, January 1, 2019
def decyear3(year, month, day, h=0, m=0, s=0) :
refyear = 2015
leapyr = ((np.r_[year]%4==0) * (np.r_[year]%100!=0) + (np.r_[year]%400==0)).astype(int)
day_of_year = np.r_[0,31,28,31,30,31,30,31,31,30,31,30,31].cumsum()
extraday = np.r_[month>2].astype(int)*leapyr
year_seconds = 31557600.0 # Weighted average of leap and non-leap years
seconds_from_ref = ((year-refyear)*year_seconds + (((( day_of_year[month-1]+extraday + day-1)*24+h)*60 + m)*60 +s))
return refyear + seconds_from_ref/year_seconds
# No library needed, one-liner that's probably good enough
def decyear4(year, month, day, h=0, m=0, s=0) :
return year + ((30.4375*(month-1) + day-1)*24+h)*3600/31557600.0
# Just for fun - empirically determined one-liner (e.g. with a linear fit)
def decyear5(year, month, day, h=0, m=0, s=0) :
return -8.789580e-02 + year + 8.331180e-02*month + 2.737750e-03*day + 1.142047e-04*hr + 2.079919e-06*mn + -1.731524e-07*sec
#
# Code to compare conversions
#
N = 500000
year = np.random.randint(1600,2050,(N))
month = np.random.randint(1,12,(N))
day = np.random.randint(1,28,(N))
hr = np.random.randint(0,23,(N))
mn = np.random.randint(0,59,(N))
sec = np.random.randint(0,59,(N))
s = ('decyear1','decyear2','decyear3','decyear4','decyear5')
decyears = np.zeros((N,len(s)))
for f, i in zip( (np.vectorize(decyear1), decyear2, decyear3, decyear4, decyear5), range(len(s)) ) :
decyears[:,i] = f(year,month,day,hr,mn,sec)
avg, std, mx = np.zeros((len(s),len(s)), 'float64'),np.zeros((len(s),len(s)), 'float64'),np.zeros((len(s),len(s)), 'float64')
for i in range(len(s)) :
for j in range(len(s)) :
avg[i,j] = np.abs(decyears[:,i]-decyears[:,j]).mean()*365*24
std[i,j] = (decyears[:,i]-decyears[:,j]).std()*365*24
mx[i,j] = np.abs(decyears[:,i]-decyears[:,j]).max()*365*24
import pandas as pd
unit = " (hours, 1 hour ~= .0001 year)"
for a,b in zip((avg, std, mx),("Average difference"+unit, "Standard dev.", "Max difference")) :
print(b+unit)
print(pd.DataFrame(a, columns=s, index=s).round(3))
print()
并且听听他们如何在伪随机日期集合上进行比较:
Average magnitude of difference (hours, 1 hour ~= .0001 year)
decyear1 decyear2 decyear3 decyear4 decyear5
decyear1 0.000 0.000 4.035 19.258 14.051
decyear2 0.000 0.000 4.035 19.258 14.051
decyear3 4.035 4.035 0.000 20.609 15.872
decyear4 19.258 19.258 20.609 0.000 16.631
decyear5 14.051 14.051 15.872 16.631 0.000
Standard dev of difference (hours, 1 hour ~= .0001 year)
decyear1 decyear2 decyear3 decyear4 decyear5
decyear1 0.000 0.000 5.402 16.550 16.537
decyear2 0.000 0.000 5.402 16.550 16.537
decyear3 5.402 5.402 0.000 18.382 18.369
decyear4 16.550 16.550 18.382 0.000 0.673
decyear5 16.537 16.537 18.369 0.673 0.000
Max difference (hours, 1 hour ~= .0001 year)
decyear1 decyear2 decyear3 decyear4 decyear5
decyear1 0.000 0.000 16.315 43.998 30.911
decyear2 0.000 0.000 16.315 43.998 30.911
decyear3 16.315 16.315 0.000 44.969 33.171
decyear4 43.998 43.998 44.969 0.000 18.166
decyear5 30.911 30.911 33.171 18.166 0.000
请注意,这些都不一定比其他的更“正确”。这取决于您的定义和用例。但是decyear1
和decyear2
可能是大多数人的想法,尽管(如上所述)它们可能由于非线性问题,在可能使用十进制年份的情况下使用。虽然所有版本都在百分之一内保持一致,但在很多情况下任何人都会这样做(例如我的情况,我需要它作为 World Magnetic Model 2020 的输入)。
问题:
希望现在很明显,精度优于一小时可能并不是真正必要的,但如果是,则可能需要针对时区和夏令时补偿您的数据。 编辑:如果您在整理出小时数后还需要另外 3 位精度,请不要忘记闰秒。
关于精度的说明:
上面给出的所有变体都表现良好且可逆 - 这意味着映射本身具有无限的精度。另一方面,准确性假设有一个特定的标准。例如,如果您在没有解释的情况下获得十进制年份,那么您所做的反向映射的准确性只能保证在半天左右的时间内。
答案 4 :(得分:0)
这比其他解决方案更简单:
import datetime
def year_fraction(date):
start = datetime.date(date.year, 1, 1).toordinal()
year_length = datetime.date(date.year+1, 1, 1).toordinal() - start
return date.year + float(date.toordinal() - start) / year_length
>>> print year_fraction(datetime.datetime.today())
2016.32513661
请注意,这会根据当天的开始计算分数,因此12月31日将为0.997,而不是1.0。
答案 5 :(得分:0)
很惊讶没有人提到过这个......但是减去datetime.timedelta
个对象产生的datetime.datetime
个对象有一个除法方法。所以,你可以使用简单的函数
from datetime import datetime
def datetime2year(dt):
year_part = dt - datetime(year=dt.year, month=1, day=1)
year_length = datetime(year=dt.year+1, month=1, day=1) - datetime(year=dt.year, month=1, day=1)
return dt.year + year_part/year_length
其中除法位于datetime.timedelta
个对象之间。
答案 6 :(得分:0)
可以使用Pandas的朱利安日期和以下公式计算小数日期。
如果您的pandas数据帧的索引是日期时间:
JD=dat.index.to_julian_date() #create julian date
L= JD+68569
N= 4*L/146097
L= L-(146097*N+3)/4
I= 4000*(L+1)/1461001
L= L-1461*I/4+31
J= 80*L/2447
K= L-2447*J/80
L= J/11
J= J+2-12*L
decimal_date= 100*(N-49)+I+L
decimal_date是一系列日期(与数据帧索引在同一个TZ中),形式类似于2007.123452。
答案 7 :(得分:-1)
如果要包括分钟和秒 使用这个:
YearF=[(x.timetuple().tm_yday-1+x.timetuple().tm_hour/24+x.timetuple().tm_min/(60*24)+x.timetuple().tm_sec/(24*3600))/(365+((x.timetuple().tm_year//4)==(x.timetuple().tm_year/4)))+x.timetuple().tm_year for x in DateArray]