Python 将缺失部分的部分H:M:S持续时间转换为秒;或右对齐非NA数据

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

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       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的方法,我想到了这个-使用Pandas
apply
和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个回路)
Numpy
argsort
+
沿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列更为稳健;因此,这些检查需要添加到其他解决方案中。