Python 将缺失部分的部分H:M:S持续时间转换为秒;或右对齐非NA数据
TL;DR:我想右对齐此df,覆盖NaN/将其向左移动: [6]中的Python 将缺失部分的部分H:M:S持续时间转换为秒;或右对齐非NA数据,python,pandas,dataframe,series,timedelta,Python,Pandas,Dataframe,Series,Timedelta,TL;DR:我想右对齐此df,覆盖NaN/将其向左移动: [6]中的:series.str.split(“:”,expand=True) 出[6]: 0 1 2 0 1 25.842 1. 2 0 15.413 3 54.154 4 3 2 06.284 要将其作为连续数据并填充最右边的列,请执行以下操作: 0 1 2 0 0
:series.str.split(“:”,expand=True)
出[6]:
0 1 2
0 1 25.842
1.
2 0 15.413
3 54.154
4 3 2 06.284
要将其作为连续数据并填充最右边的列,请执行以下操作:
0 1 2
0 0 1 25.842 # 0 or NA
1 <NA> <NA> <NA> # this NA should remain
2 0 0 15.413
3 0 0 54.154
4 3 2 06.284
如果我不执行此fillna(0)
步骤,那么它将生成NaN,用于稍后的秒转换
[4]中的:smh.iloc[:,1:]=smh.iloc[:,1:].fillna(0)#第一列中的NaN=NaN来自数据;所以离开
…:#转换为秒
…:smh.iloc[:,0]+smh.iloc[:,1]*60+smh.iloc[:,2]*3600
出[4]:
0 85.842
1楠
2 15.413
3 54.154
4 10926.284
数据类型:64
^预期的最终结果
(或者,我可以编写一个只支持Python的小函数来拆分
:
,然后根据每个列表的值数进行转换。)让我们尝试使用numpy
右对齐数据帧,基本思想是沿轴=1对数据帧进行排序,使NaN
值出现在非NaN
值之前,同时保持非NaN
值的顺序不变:
i = np.argsort(np.where(df.isna(), -1, 0), 1)
df[:] = np.take_along_axis(df.values, i, axis=1)
0 1 2
0 NaN 1.0 25.842
1 NaN NaN NaN
2 NaN 0.0 15.413
3 NaN NaN 54.154
4 3.0 2.0 6.284
为了获得总秒数
,您可以将右对齐的数据帧乘以[3600,60,1]
,然后沿轴=1取和
:
df.mul([3600, 60, 1]).sum(1)
0 85.842
1 0.000
2 15.413
3 54.154
4 10926.284
dtype: float64
您可以通过使用'0:'
填充系列
来解决此问题,如下所示:
# setup
series = pd.Series(['1:25.842', pd.NA, '0:15.413', '54.154', '3:2:06.284'], dtype='string')
# create a padding of 0 series
counts = 2 - series.str.count(':')
pad = pd.Series(['0:' * c if pd.notna(c) and c > 0 else '' for c in counts], dtype='string')
# apply padding
res = pad.str.cat(series)
t = res.str.split(':', expand=True)
print(t)
输出
0 1 2
0 0 1 25.842
1 <NA> <NA> <NA>
2 0 0 15.413
3 0 0 54.154
4 3 2 06.284
0112
0 0 1 25.842
1.
2 0 0 15.413
3 0 0 54.154
4 3 2 06.284
1.使用排序NA的方法,我想到了这个-使用Pandasapply
和Python排序
:
series=pd.series([1:25.842',pd.NA',0:15.413',54.154',3:2:06.284',dtype='string')
df=series.str.split(“:”,expand=True)
#排序的键是'pd.notna',因此False(0)在True(1)之前排序
应用(已排序,轴=1,键=pd.notna,结果\u type='broadcast')
(然后根据需要进行乘法运算)但速度相当慢,请参见下文
2.通过预先填充“0:”,我可以直接创建pd.Timedelta
,并获取它们的总秒数
:
res=…#从答案
pd.to_timedelta(res,errors='concurve').map(lambda x:x.total_seconds())
(但在~10k行中,先进行展开拆分,然后进行乘法+求和的速度更快。)
性能注意事项,包含10000行数据:
我问题中的初始代码/尝试,行反转-所以我可能会坚持:
%%timeit
t=series.str.split(“:”)
行=[i[::-1]如果i不是pd.NA else[]表示t中的i]
smh=pd.DataFrame.from_记录(行).astype('float'))
smh.mul([1,60,3600]).sum(轴=1,最小计数=1)
#每个回路14.3 ms±310µs(7次运行的平均值±标准偏差,每个100个回路)
Numpyargsort
+沿u轴取u
:
%%timeit
df=series.str.split(“:”,expand=True)
i=np.argsort(np.where(df.isna(),-1,0),1)
df[:]=np.沿_轴取_(df.values,i,axis=1)
df.apply(pd.to_numeric,errors='concurve').mul([3600,60,1]).sum(axis=1,min_count=1)
#每个回路30.1 ms±1.03 ms(7次运行的平均值±标准偏差,每个10个回路)
预先填充:
%%timeit
计数=2-series.str.count(“:”)
pad=pd.Series(['0:'*c如果pd.notna(c)else''表示计数中的c],dtype='string')
res=衬垫str.cat(系列)
t=res.str.split(“:”,expand=True)
t、 apply(pd.to_numeric,errors='concurve').mul([3600,60,1]).sum(axis=1,min_count=1)
#每个回路48.3 ms±607µs(7次运行的平均值±标准偏差,每个10个回路)
预先填充,时间增量+总秒数:
%%timeit
计数=2-series.str.count(“:”)
pad=pd.Series(['0:'*c如果pd.notna(c)else''表示计数中的c],dtype='string')
res=衬垫str.cat(系列)
pd.to_timedelta(res,errors='concurve').map(lambda x:x.total_seconds())
#每个回路183 ms±9.83 ms(7次运行的平均值±标准偏差,每个10个回路)
熊猫应用
+Python排序
(非常慢):
%%timeit
df=series.str.split(“:”,expand=True)
df=df.apply(已排序,轴=1,键=pd.notna,结果\u type='broadcast')
df.apply(pd.to_numeric).mul([3600,60,1]).sum(轴=1,最小计数=1)
#每个回路1.4 s±36.7 ms(7次运行的平均值±标准偏差,每个回路1次)
感谢您添加了[3600,60,1]
乘法-这就是我最初希望使用右对齐df所做的。(除了我需要将所有0替换回NaN之外。)@aneroid没问题。快乐编码:)预先填充/facepalm。是的,那会更快更直接。顺便说一句,和c>0
部分可以跳过,因为'abc'*0=''
。对于未来的访问者:请参阅下面我的答案中的。可接受的答案是,基于完整的输入序列,将wrt拆分为2列或3列更为稳健;因此,这些检查需要添加到其他解决方案中。