Pandas 时间和日期时间混合的列有问题
Pandas problem with a column with mixed time and date time
我有一个来自 Excel 的列,它应该包含持续时间(以小时为单位)- 示例:02:00:00
-
如果所有这些持续时间都小于 24:00,它会很好地工作,但如果超过一个,它会在 pandas 中显示为 1900-01-03 08:00:00
(所以日期时间)
结果数据类型是 dtype('O').
df = pd.DataFrame({'duration':[datetime.time(2, 0), datetime.time(2, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0), datetime.time(1, 0),
datetime.time(1, 0)]})
# Output
duration
0 02:00:00
1 02:00:00
2 1900-01-03 08:00:00
3 1900-01-03 08:00:00
4 1900-01-03 08:00:00
5 1900-01-03 08:00:00
6 1900-01-03 08:00:00
7 1900-01-03 08:00:00
8 01:00:00
9 01:00:00
但是如果我尝试转换为时间或日期时间,我总是会收到错误消息。
TypeError: <class 'datetime.time'> is not convertible to datetime
今天如果我不解决这个问题,所有超过 24:00 的持续时间都将消失。
IIUC,使用pd.to_timedelta
:
设置一个MRE:
df = pd.DataFrame({'duration': ['43:24:57', '22:12:52', '-', '78:41:33']})
print(df)
# Output
duration
0 43:24:57
1 22:12:52
2 -
3 78:41:33
df['duration'] = pd.to_timedelta(df['duration'], errors='coerce')
print(df)
# Output
duration
0 1 days 19:24:57
1 0 days 22:12:52
2 NaT
3 3 days 06:41:33
更新
@MrFuppes Excel file is exactly what I have in my column 'duration'
尝试:
df['duration'] = np.where(df['duration'].apply(len) == 8,
'1899-12-31 ' + df['duration'], df['duration'])
df['duration'] = pd.to_datetime(df['duration'], errors='coerce') \
- pd.Timestamp('1899-12-31')
print(df)
# Output (with a slightly modified example of @MrFuppes)
duration
0 0 days 12:30:00
1 1 days 00:30:00
2 NaT
你的问题出在读取Excel文件的引擎上。它将具有特定格式(例如 [h]:mm:ss
或 hh:mm:ss
)的单元格转换为 datetime.datetime
或 datetime.time
对象。然后那些被转移到 pandas DataFrame,所以它实际上不是 pandas 问题。
在开始破解 excel reader 引擎之前,解决 Excel 中的问题可能更容易。这是一个小示例文件;
可以下载here。
duration
是 auto-formatted by Excel,duration_text
是将列格式设置为 'text' before 后得到的结果您输入值,duration_to_text
是您将格式更改为文本 在 Excel auto-formatted 值(第一列)之后得到的结果。
现在您在使用 pandas:
导入后拥有了所需的一切
df = pd.read_excel('path_to_file')
df
duration duration_text duration_to_text
0 12:30:00 12:30:00 0.520833
1 1900-01-01 00:30:00 24:30:00 1.020833
# now you can parse to timedelta:
pd.to_timedelta(df['duration_text'], errors='coerce')
0 0 days 12:30:00
1 1 days 00:30:00
Name: duration_text, dtype: timedelta64[ns]
# or
pd.to_timedelta(df['duration_to_text'], unit='d', errors='coerce')
0 0 days 12:29:59.999971200 # note the precision issue ;-)
1 1 days 00:29:59.999971200
Name: duration_to_text, dtype: timedelta64[ns]
另一个可行的选择是将 Excel 文件保存为 csv
并将其导入到 pandas DataFrame。例如,上面使用的示例 xlsx 看起来像 this。
如果除了 pandas 中的 re-process 别无选择,一个选项可能是专门处理 datetime.time 对象和 datetime.datetime 对象,例如
import datetime
# where you have datetime (incorrect from excel)
m = [isinstance(i, datetime.datetime) for i in df.duration]
# convert to timedelta where it's possible
df['timedelta'] = pd.to_timedelta(df['duration'].astype(str), errors='coerce')
# where you have datetime, some special treatment is needed...
df.loc[m, 'timedelta'] = df.loc[m, 'duration'].apply(lambda t: pd.Timestamp(str(t)) - pd.Timestamp('1899-12-31'))
df['timedelta']
0 0 days 12:30:00
1 1 days 00:30:00
Name: timedelta, dtype: timedelta64[ns]
我有一个来自 Excel 的列,它应该包含持续时间(以小时为单位)- 示例:02:00:00
-
如果所有这些持续时间都小于 24:00,它会很好地工作,但如果超过一个,它会在 pandas 中显示为 1900-01-03 08:00:00
(所以日期时间)
结果数据类型是 dtype('O').
df = pd.DataFrame({'duration':[datetime.time(2, 0), datetime.time(2, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0),
datetime.datetime(1900, 1, 3, 8, 0), datetime.time(1, 0),
datetime.time(1, 0)]})
# Output
duration
0 02:00:00
1 02:00:00
2 1900-01-03 08:00:00
3 1900-01-03 08:00:00
4 1900-01-03 08:00:00
5 1900-01-03 08:00:00
6 1900-01-03 08:00:00
7 1900-01-03 08:00:00
8 01:00:00
9 01:00:00
但是如果我尝试转换为时间或日期时间,我总是会收到错误消息。
TypeError: <class 'datetime.time'> is not convertible to datetime
今天如果我不解决这个问题,所有超过 24:00 的持续时间都将消失。
IIUC,使用pd.to_timedelta
:
设置一个MRE:
df = pd.DataFrame({'duration': ['43:24:57', '22:12:52', '-', '78:41:33']})
print(df)
# Output
duration
0 43:24:57
1 22:12:52
2 -
3 78:41:33
df['duration'] = pd.to_timedelta(df['duration'], errors='coerce')
print(df)
# Output
duration
0 1 days 19:24:57
1 0 days 22:12:52
2 NaT
3 3 days 06:41:33
更新
@MrFuppes Excel file is exactly what I have in my column 'duration'
尝试:
df['duration'] = np.where(df['duration'].apply(len) == 8,
'1899-12-31 ' + df['duration'], df['duration'])
df['duration'] = pd.to_datetime(df['duration'], errors='coerce') \
- pd.Timestamp('1899-12-31')
print(df)
# Output (with a slightly modified example of @MrFuppes)
duration
0 0 days 12:30:00
1 1 days 00:30:00
2 NaT
你的问题出在读取Excel文件的引擎上。它将具有特定格式(例如 [h]:mm:ss
或 hh:mm:ss
)的单元格转换为 datetime.datetime
或 datetime.time
对象。然后那些被转移到 pandas DataFrame,所以它实际上不是 pandas 问题。
在开始破解 excel reader 引擎之前,解决 Excel 中的问题可能更容易。这是一个小示例文件;
可以下载here。
duration
是 auto-formatted by Excel,duration_text
是将列格式设置为 'text' before 后得到的结果您输入值,duration_to_text
是您将格式更改为文本 在 Excel auto-formatted 值(第一列)之后得到的结果。
现在您在使用 pandas:
导入后拥有了所需的一切df = pd.read_excel('path_to_file')
df
duration duration_text duration_to_text
0 12:30:00 12:30:00 0.520833
1 1900-01-01 00:30:00 24:30:00 1.020833
# now you can parse to timedelta:
pd.to_timedelta(df['duration_text'], errors='coerce')
0 0 days 12:30:00
1 1 days 00:30:00
Name: duration_text, dtype: timedelta64[ns]
# or
pd.to_timedelta(df['duration_to_text'], unit='d', errors='coerce')
0 0 days 12:29:59.999971200 # note the precision issue ;-)
1 1 days 00:29:59.999971200
Name: duration_to_text, dtype: timedelta64[ns]
另一个可行的选择是将 Excel 文件保存为 csv
并将其导入到 pandas DataFrame。例如,上面使用的示例 xlsx 看起来像 this。
如果除了 pandas 中的 re-process 别无选择,一个选项可能是专门处理 datetime.time 对象和 datetime.datetime 对象,例如
import datetime
# where you have datetime (incorrect from excel)
m = [isinstance(i, datetime.datetime) for i in df.duration]
# convert to timedelta where it's possible
df['timedelta'] = pd.to_timedelta(df['duration'].astype(str), errors='coerce')
# where you have datetime, some special treatment is needed...
df.loc[m, 'timedelta'] = df.loc[m, 'duration'].apply(lambda t: pd.Timestamp(str(t)) - pd.Timestamp('1899-12-31'))
df['timedelta']
0 0 days 12:30:00
1 1 days 00:30:00
Name: timedelta, dtype: timedelta64[ns]