203

今天,当我从数据文件(例如)读取数据时,pandas 能够识别值的类型,这让我感到非常惊讶:

df = pandas.read_csv('test.dat', delimiter=r"\s+", names=['col1','col2','col3'])

例如可以这样检查:

for i, r in df.iterrows():
    print type(r['col1']), type(r['col2']), type(r['col3'])

特别是整数、浮点数和字符串被正确识别。但是,我有一列具有以下格式的日期:2013-6-4. 这些日期被识别为字符串(而不是 python 日期对象)。有没有办法“学习”熊猫来识别日期?

4

11 回答 11

408

您应该添加parse_dates=True,或者parse_dates=['column name']在阅读时,通常足以神奇地解析它。但是总是有一些奇怪的格式需要手动定义。在这种情况下,您还可以添加日期解析器功能,这是可能的最灵活的方式。

假设您的字符串中有一个“日期时间”列,那么:

from datetime import datetime
dateparse = lambda x: datetime.strptime(x, '%Y-%m-%d %H:%M:%S')

df = pd.read_csv(infile, parse_dates=['datetime'], date_parser=dateparse)

通过这种方式,您甚至可以将多个列合并到一个日期时间列中,这会将一个“日期”和一个“时间”列合并到一个“日期时间”列中:

dateparse = lambda x: datetime.strptime(x, '%Y-%m-%d %H:%M:%S')

df = pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)

您可以在此页面strptime中找到和的指令(即用于不同格式的字母)。strftime

于 2013-07-04T10:32:01.837 回答
27

自@Rutger 回答以来,pandas 界面可能发生了变化,但在我使用的版本(0.15.2)中,该date_parser函数接收日期列表而不是单个值。在这种情况下,他的代码应该像这样更新:

from datetime import datetime
import pandas as pd

dateparse = lambda dates: [datetime.strptime(d, '%Y-%m-%d %H:%M:%S') for d in dates]
    
df = pd.read_csv('test.dat', parse_dates=['datetime'], date_parser=dateparse)

由于最初的问题提问者说他​​想要日期并且日期是2013-6-4格式的,所以dateparse函数应该是:

dateparse = lambda dates: [datetime.strptime(d, '%Y-%m-%d').date() for d in dates]
于 2015-03-11T16:03:34.387 回答
17

您可以pandas.to_datetime()按照文档中的建议使用pandas.read_csv()

如果列或索引包含不可解析的日期,则整个列或索引将作为对象数据类型原封不动地返回。对于非标准日期时间解析,使用pd.to_datetimeafter pd.read_csv

演示:

>>> D = {'date': '2013-6-4'}
>>> df = pd.DataFrame(D, index=[0])
>>> df
       date
0  2013-6-4
>>> df.dtypes
date    object
dtype: object
>>> df['date'] = pd.to_datetime(df.date, format='%Y-%m-%d')
>>> df
        date
0 2013-06-04
>>> df.dtypes
date    datetime64[ns]
dtype: object
于 2017-09-24T12:52:02.737 回答
13

将两列合并为一个日期时间列时,接受的答案会产生错误(pandas 版本 0.20.3),因为这些列是分别发送到 date_parser 函数的。

以下作品:

def dateparse(d,t):
    dt = d + " " + t
    return pd.datetime.strptime(dt, '%d/%m/%Y %H:%M:%S')

df = pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)
于 2017-10-25T08:54:03.967 回答
10

pandas read_csv 方法非常适合解析日期。完整文档位于http://pandas.pydata.org/pandas-docs/stable/generated/pandas.io.parsers.read_csv.html

您甚至可以在不同的列中有不同的日期部分并传递参数:

parse_dates : boolean, list of ints or names, list of lists, or dict
If True -> try parsing the index. If [1, 2, 3] -> try parsing columns 1, 2, 3 each as a
separate date column. If [[1, 3]] -> combine columns 1 and 3 and parse as a single date
column. {‘foo’ : [1, 3]} -> parse columns 1, 3 as date and call result ‘foo’

日期的默认感知效果很好,但它似乎偏向于北美日期格式。如果您住在其他地方,您可能偶尔会被结果所吸引。据我所知,2000 年 1 月 6 日在美国是指 1 月 6 日,而不是我居住的 6 月 1 日。如果使用像 2000 年 6 月 23 日这样的日期,将它们左右摆动是足够聪明的。不过,使用 YYYYMMDD 日期变化可能更安全。在这里向 pandas 开发人员道歉,但我最近没有用本地日期对其进行测试。

您可以使用 date_parser 参数传递一个函数来转换您的格式。

date_parser : function
Function to use for converting a sequence of string columns to an array of datetime
instances. The default uses dateutil.parser.parser to do the conversion.
于 2013-07-04T10:38:47.637 回答
8

是的 - 根据pandas.read_csv 文档

注意: iso8601 格式的日期存在快速路径。

因此,如果您的 csv 有一个名为的列datetime并且日期看起来像2013-01-01T01:01例如,运行它将使 pandas(我在 v0.19.2 上)自动获取日期和时间:

df = pd.read_csv('test.csv', parse_dates=['datetime'])

请注意,您需要显式传递parse_dates,没有它就无法工作。

验证:

df.dtypes

您应该看到该列的数据类型是datetime64[ns]

于 2017-04-10T02:46:30.413 回答
4

加载 csv 文件时包含日期列。我们有两种方法可以让熊猫识别日期列,即

  1. Pandas 通过 arg 显式识别格式date_parser=mydateparser

  2. Pandas 通过 agr 隐式识别格式infer_datetime_format=True

部分日期列数据

2018 年 1 月 1 日

2018 年 1 月 2 日

这里我们不知道前两件事可能是一个月或一天。所以在这种情况下,我们必须使用方法 1:- 显式传递格式

    mydateparser = lambda x: pd.datetime.strptime(x, "%m/%d/%y")
    df = pd.read_csv(file_name, parse_dates=['date_col_name'],
date_parser=mydateparser)

方法 2:- 隐式或自动识别格式

df = pd.read_csv(file_name, parse_dates=[date_col_name],infer_datetime_format=True)
于 2019-09-20T19:30:40.173 回答
2

除了其他回复所说的之外,如果您必须解析具有数十万个时间戳的非常大的文件,date_parser可能会成为一个巨大的性能瓶颈,因为它是一个每行调用一次的 Python 函数。通过在解析 CSV 文件时将日期保留为文本,然后一次性将整个列转换为日期,您可以获得相当大的性能改进:

# For a data column
df = pd.read_csv(infile, parse_dates={'mydatetime': ['date', 'time']})

df['mydatetime'] = pd.to_datetime(df['mydatetime'], exact=True, cache=True, format='%Y-%m-%d %H:%M:%S')
# For a DateTimeIndex
df = pd.read_csv(infile, parse_dates={'mydatetime': ['date', 'time']}, index_col='mydatetime')

df.index = pd.to_datetime(df.index, exact=True, cache=True, format='%Y-%m-%d %H:%M:%S')
# For a MultiIndex
df = pd.read_csv(infile, parse_dates={'mydatetime': ['date', 'time']}, index_col=['mydatetime', 'num'])

idx_mydatetime = df.index.get_level_values(0)
idx_num = df.index.get_level_values(1)
idx_mydatetime = pd.to_datetime(idx_mydatetime, exact=True, cache=True, format='%Y-%m-%d %H:%M:%S')
df.index = pd.MultiIndex.from_arrays([idx_mydatetime, idx_num])

对于我在具有 200k 行(每行一个时间戳)的文件上的用例,这将处理时间从大约一分钟减少到不到一秒。

于 2020-08-10T17:30:12.710 回答
1

如果性能对您很重要,请确保您的时间:

import sys
import timeit
import pandas as pd

print('Python %s on %s' % (sys.version, sys.platform))
print('Pandas version %s' % pd.__version__)

repeat = 3
numbers = 100

def time(statement, _setup=None):
    print (min(
        timeit.Timer(statement, setup=_setup or setup).repeat(
            repeat, numbers)))

print("Format %m/%d/%y")
setup = """import pandas as pd
import io

data = io.StringIO('''\
ProductCode,Date
''' + '''\
x1,07/29/15
x2,07/29/15
x3,07/29/15
x4,07/30/15
x5,07/29/15
x6,07/29/15
x7,07/29/15
y7,08/05/15
x8,08/05/15
z3,08/05/15
''' * 100)"""

time('pd.read_csv(data); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"]); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'infer_datetime_format=True); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'date_parser=lambda x: pd.datetime.strptime(x, "%m/%d/%y")); data.seek(0)')

print("Format %Y-%m-%d %H:%M:%S")
setup = """import pandas as pd
import io

data = io.StringIO('''\
ProductCode,Date
''' + '''\
x1,2016-10-15 00:00:43
x2,2016-10-15 00:00:56
x3,2016-10-15 00:00:56
x4,2016-10-15 00:00:12
x5,2016-10-15 00:00:34
x6,2016-10-15 00:00:55
x7,2016-10-15 00:00:06
y7,2016-10-15 00:00:01
x8,2016-10-15 00:00:00
z3,2016-10-15 00:00:02
''' * 1000)"""

time('pd.read_csv(data); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"]); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'infer_datetime_format=True); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
     'date_parser=lambda x: pd.datetime.strptime(x, "%Y-%m-%d %H:%M:%S")); data.seek(0)')

印刷:

Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 03:13:28) 
[Clang 6.0 (clang-600.0.57)] on darwin
Pandas version 0.23.4
Format %m/%d/%y
0.19123052499999993
8.20691274
8.143124389
1.2384357139999977
Format %Y-%m-%d %H:%M:%S
0.5238807110000039
0.9202787830000005
0.9832778819999959
12.002349824999996

因此,对于 iso8601 格式的日期(%Y-%m-%d %H:%M:%S显然是 iso8601 格式的日期,我猜 T可以被删除并用空格替换)你不应该指定infer_datetime_format这显然与更常见的日期没有区别)并传递你自己的解析器只会削弱性能。另一方面,date_parser使用不那么标准的日期格式确实会有所不同。像往常一样,一定要在优化之前计时。

于 2018-12-04T22:32:53.043 回答
0

您可以将参数date_parser与用于将字符串列序列转换为日期时间实例数组的函数一起使用:

parser = lambda x: pd.to_datetime(x, format='%Y-%m-%d %H:%M:%S')
pd.read_csv('path', date_parser=parser, parse_dates=['date_col1', 'date_col2'])
于 2021-05-20T15:54:35.273 回答
0

不,pandas 无法自动识别日期列。

Pandas 在类型推断方面做得很差。它基本上将大多数列作为通用object类型,除非您手动解决它,例如。使用上述parse_dates参数。

如果要自动检测列类型,则必须使用单独的数据分析工具,例如。visions,然后将推断的类型转换或反馈回您的DataFrame构造函数(例如,对于 dates 和from_csv,使用parse_dates参数)。

于 2021-07-19T16:19:11.460 回答