想要解决的问题:每个人入院日期条两两对比,需要找出间隔小于3天的数据行删除。如某某入院日期分别为“2023-01-01,2023-02-07,2023-02-08,2023-05-01,2023-05-02”,需要删除其中2023-02-08,2023-05-02数据所在行。
代码如下,我运行成功后没有达到想要的效果,请问是什么原因?怎么修改避免?非常期待各位的解答~
import pandas as pd
from datetime import datetime, timedelta
# 读取Excel数据
df = pd.read_excel('原始数据.xlsx')
# 将入住时间列转换为datetime类型
df['入院日期'] = pd.to_datetime(df['入院日期'],format='"%Y-%m-%d"',errors='coerce')
# 按姓名列分组
grouped = df.groupby('姓名')
# 定义函数用于处理每个分组
def process_group(group):
# 对每个分组按入院日期列升序排序
group = group.sort_values('入院日期')
# 初始化索引和循环标志
index = 0
keep_looping = True
while keep_looping:
if index >= len(group) - 1:
# 已经到达分组末尾,退出循环
keep_looping = False
else:
# 当前行和下一行的入院日期差值
time_diff = group.iloc[index+1]['入院日期']
if time_diff <= timedelta(days=3):
# 入院日期相隔小,删除下一行数据
group = group.drop(group.index[index+1])
else:
index += 1
# 入院日期相隔大于三天,索引加一继续对 += 1
return group
# 使用apply方法对每个分处理函数
processed_df = grouped.apply(process_group)
# 将处理后的数据保存为新的Excel文件
processed_df.to_excel('processed_file.xlsx', index=False)
不知道你这个问题是否已经解决, 如果还没有解决的话:1. 时间戳转datetime
常用场景:分析时需要将数据date字段存储的数字转为直观形式
datetime.fromtimestamp(1580572800.0) #输出:datetime.datetime(2020, 2, 2, 0, 0)
datetime.utcfromtimestamp(1580572800.0) #输出: datetime.datetime(2020, 2, 1, 16, 0)
可以发现同一个时间戳数据1580572800.0,fromtimestamp是在localtime背景下,将1970年01月01日08时00分00秒视为起点,将1580572800.0视为总秒数,算出来是2020, 2, 2, 0, 0。utcfromtimestamp是将1970年01月01日00时00分00秒视为起点,所以转换后比上一个时间晚8个小时。
datetime.fromtimestamp(9999999999) #datetime.datetime(2286, 11, 21, 1, 46, 39)
扩展知识:time.ctime函数可以把一个时间戳(按秒计算的浮点数)转化为time.asctime()形式的str。
time.ctime(1580572800.0) #输出'Sun Feb 2 00:00:00 2020'
2. 时间转时间戳
常用场景:需要将一个时间存储为时间戳,然后存储在数据表中。
(1)获得localtime的时间戳,最简单方法:time.time()
(2)把一个任意的datetime时间转时间戳float:
具体过程:datetime–time.struct_time–float
time.mktime方法将struct_time时间转为时间戳,需要将datetime时间先通过timetuple()函数转为struct_time类型
s = time.mktime(datetime(2020,2,2).timetuple())
print(s)
问题:Python为什么需要struct_time类型做转换中介?
datetime时间虽然直观,但不能subscriptable(通过index或下标访问),并且datetime object is not iterable。所以当要取一个时刻的年、月、日等子特征时就比较麻烦。如果将其转换为time.struct_time类型后,就可以很方便根据索引或下标形式访问时间子特征。
datetime.now().timetuple().tm_year #当前时刻所在年
datetime.now().timetuple()[1] #当前时刻所在月份
struct_time包括的9个属性:
tm_year 年
tm_mon 月,范围1~12
tm_mday 日,范围1~31
tm_hour 小时,范围0~23
tm_min 分钟,范围0~59
tm_sec 秒,范围0~61(60或61是闰秒)
tm_wday 星期,范围0~6(周一为0)
tm_yday 一年内第几天,范围0~366
tm_isdst 夏时令,-1,0,1
mktime的逆函数为localtime和gmtime,既然是逆方法,所以返回值为struct_time类型。
s = time.mktime(datetime(2020,2,2).timetuple()) #获取时间戳
time.localtime(s) #时间戳转local时间
time.gmtime(s) #时间戳转格林威治时间
time.gmtime(s)[0] #获得tm_year子特征
3.time_struct转datetime
一种方法是先通过time.mktime转为时间戳,然后通过datetime.fromtimestamp转为datetime。更简便的方法如下:
structTime = time.localtime() #time.localtime()返回time_struct格式的当前时间
datetime(*structTime[:6])
time_struct类似于tuple,tuple是可以切片的,structTime[:6]的type是tuple。星号出现在函数的参数structTime[:6]前面,代表解包。即在列表、元组、集合、字典及其他可迭代对象作为实参,并在前面加*时,系统自动进行解包然后传递给函数的多个单变量参数。
而如果:
datetime(*structTime)
会提示:TypeError: function takes at most 8 positional arguments (9 given),思考一下为什么会出现异常?
4. pandas的pd.to_datetime方法【重点】
(1)函数定义
pandas.to_datetime(arg, errors=‘ignore’, dayfirst=False, utc=None, box=True, format=None, exact=True, coerce=False, unit=‘ns’, infer_datetime_format=False)
该函数常用于原始数据中date字段的转换。函数的返回值依据于输入数据的类型,输入输出对应关系如下:
list-like: DatetimeIndex
Series: Series of datetime64 dtype
scalar: Timestamp
(2)转换实例
pandas读取源数据时,比如csv文件,有可能将日期时间特征在定义为object类型。
举例1:
s = pd.Series('01/02/1965',dtype='object')
d = pd.to_datetime(s,format='%m/%d/%Y')
print(d)
运行结果:
0 1965-01-02
dtype: datetime64[ns]
这个例子中,format=’%m/%d/%Y’参数不写也可。
s = pd.Series('01/02/1965',dtype='object')
d = pd.to_datetime(s)
print(d)
这是因为pandas能猜测出来其为1965年1月2日,但如果日期时间的格式不太符合常理或pandas猜不准情况下就会产生异常。这时需要程序员指定日期时间的格式。
举例2:
data = pd.DataFrame({'date':['02/Apr/2013:23:55:00 +0530']})
pd.to_datetime(data['date'],format='%d/%b/%Y:%H:%M:%S %z')
源数据中的时间’02/Apr/2013:23:55:00 +0530’,如果直接不指定format参数,pandas将无法进行转换为时间。其中的+0530为时区信息,表示相对格林威治时间提前5小时30分钟,即19800秒。
如果提示错误:‘z’ is a bad directive in format ‘%d/%b/%Y:%H:%M:%S %z’,请升级pandas版本至0.24.2及以上版本,对应命令为:‘conda update pandas’。
(3)两个坑:
坑1:原数据如果是时间戳,注意参数unit默认为ns,需要根据情况判断是否将其改为s。
坑2:转换后的时间极有可能不包含时区属性,如果原数据集是我们国内的数据集,分析时直接用pd.to_datetime转换后,这个时间可能要比我们自认为的时间要晚8个小时。所以用pd.DataFrame转换时间戳时,除非这是一份来自英国这种使用格林威治时间国家的数据,一般都需要用Timestamp.tz_localize方法为没有时区的时间序列赋予时区,然后调用tz_convert函数将其转换到原数据集对应的时区。
t = pd.to_datetime(1580572800.0,unit='s') #Timestamp('2020-02-01 16:00:00')
t = t.tz_localize('UTC') #为没有时区的时间序列赋予时区,UTC代表协调世界时,又称为世界统一时间
t.tz_convert('Asia/Shanghai') #指定到特定时区后,可以用tz_convert将其转换到其它时区
#上述三行代码可简化为:
t = pd.to_datetime(1580572800.0,unit='s',utc=True) #utc默认值为None
t.tz_convert('Asia/Shanghai') #转换为北京时间
其它时区,可import pytz后,通过pytz.common_timezones来查看
import pytz
pytz.common_timezones[-6:]
之前的datetime.now(),默认创建localtime时间,如果要创建格林威治时间,可以如下操作:
datetime.now(pytz.utc) #输出:datetime.datetime(2020, 2, 9, 14, 22, 25, 565437, tzinfo=<UTC>) 参数写为pytz.UTC也可。
(4)扩展阅读:
协调世界时(英:Coordinated Universal Time ,法:Temps Universel Coordonné),又称世界统一时间,世界标准时间,国际协调时间。英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称UTC。
世界标准时间UTC:GMT+0
GMT:格林尼治平时
即格林威治时间
CST时间:也就是北京时间
北京时间CST:GMT+8
5. 将pandas中的 Timestamp转换为datetime
Timestamp是从Python标准库的datetime类继承过来的,表示时间轴上的一个时刻。分辨率为纳秒,64位整数存储,其最小时刻和最大时刻如下:
pd.Timestamp.min
#Timestamp('1677-09-21 00:12:43.145225')
pd.Timestamp.max
#Timestamp('2262-04-11 23:47:16.854775807')
所以如下代码会报错:
pd.to_datetime('13000101', format='%Y%m%d')
解决方案为:
pd.to_datetime('13000101', format='%Y%m%d', errors='ignore')
#datetime.datetime(1300, 1, 1, 0, 0)
或:
pd.to_datetime('13000101', format='%Y%m%d', errors='coerce')
#NaT
pd.to_pydatetime方法可以Convert a Timestamp object to a native Python datetime object.
pd.Timestamp.to_pydatetime(pd.to_datetime('2020/2/3'))
6.字符串到时间类型的转换
(1)字符串到struct_time
调用方法: time.strptime(string[, format])
python中日期时间格式化符号:
%y 两位数的年份表示(00-99)
%Y 四位数的年份表示(000-9999)
%m 月份(01-12)
%d 月内中的一天(0-31)
%H 24小时制小时数(0-23)
%I 12小时制小时数(01-12)
%M 分钟数(00=59)
%S 秒(00-59)
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天(001-366)
%p 本地A.M.或P.M.的等价符
%U 一年中的星期数(00-53)星期天为星期的开始
%w 星期(0-6),星期天为星期的开始
%W 一年中的星期数(00-53)星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身
time.strptime('2020-02-05 13:45:20','%Y-%m-%d %H:%M:%S')
结果为:
time.struct_time(tm_year=2020, tm_mon=2, tm_mday=5, tm_hour=13, tm_min=45, tm_sec=20, tm_wday=2, tm_yday=36, tm_isdst=-1)
(2)struct_time到字符串
t = time.strptime('2020-02-05 13:45:20','%Y-%m-%d %H:%M:%S')
s = time.strftime('%Y-%m-%d',t)
print(s) #输出2020-02-05
各种时间类型转换示意图如下图所示。
上图中,其实datetime和字符串之间其实还可以画出来两条线,datetime.strptime和datetime.strftime,功能自行百度。