【问题标题】:Can pandas automatically read dates from a CSV file?pandas 可以自动从 CSV 文件中读取日期吗?
【发布时间】:2013-07-02 03:35:37
【问题描述】:

今天,我非常惊讶的是,在从数据文件(例如)读取数据时,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 日期对象)。有没有办法让熊猫“学习”识别日期?

【问题讨论】:

    标签: python date types dataframe pandas


    【解决方案1】:

    你应该在阅读时添加parse_dates=Trueparse_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)
    

    您可以找到 strptimestrftime in this page 的指令(即用于不同格式的字母)。

    【讨论】:

    • 对我不起作用,我收到以下错误:TypeError: strptime() argument 1 must be str, not float
    • 我收到此错误是因为我的数据框中有 nan。
    • 有一个选项infer_datetime_format:“pandas 将尝试推断列中日期时间字符串的格式”。这可以用来代替date_parser
    • 请注意,如果您的日期是 ISO 8601 格式,则不应传递 infer_datetime_format 或解析器函数 - 这比让 pandas 处理它要慢得多(尤其是后者)。此答案中的日期格式也属于此类
    • pd.datetime 目前已弃用,在import datetime from datetime 之后仅将pd.datetime 替换为datetime
    【解决方案2】:

    也许自从@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]
    

    【讨论】:

      【解决方案3】:

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

      如果列或索引包含无法解析的日期,则整个列 或索引将作为对象数据类型原样返回。为了 非标准日期时间解析,在pd.read_csv之后使用pd.to_datetime

      演示:

      >>> 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
      

      【讨论】:

      • 它也将其他列转换为对象类型的日期
      【解决方案4】:

      当将两列合并为一个日期时间列时,接受的答案会产生错误(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)
      

      【讨论】:

      • 我正在使用 pandas 0.22 并同意接受的答案不再有效。
      • 这会为我创建一个“TypeError: can only concatenate str (not "float") to str"。日期列是 d/m/y,时间列是 H:M:00
      【解决方案5】:

      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.
      

      【讨论】:

      【解决方案6】:

      是的 - 根据pandas.read_csv documentation

      注意:iso8601-formatted 日期存在快速路径。

      因此,如果您的 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]

      【讨论】:

      • 我认为您误解了这个问题。用户很好奇是否可以为他的字符串格式启用该选项。
      • @AryaMcCarthy 嗯,他基本上是希望日期能够被正确识别,所以我提到他如何转换源数据以便它被熊猫自然识别。他没有提到他不能更改源数据的格式。
      【解决方案7】:

      加载 csv 文件时包含日期列。我们有两种方法来制作 pandas 识别日期列,即

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

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

      部分日期列数据

      01/01/18

      01/02/18

      这里我们不知道前两件事可能是一个月或一天。所以在这种情况下,我们必须使用 方法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)
      

      【讨论】:

        【解决方案8】:

        除了其他回复所说的之外,如果您必须解析具有数十万个时间戳的非常大的文件,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 行(每行一个时间戳)的文件的用例,这将处理时间从大约一分钟减少到不到一秒。

        【讨论】:

        • 您是否尝试过 read_csv 的 infer_datetime_format 参数。如果您的日期格式一致,它会推断出日期。它加快了这个过程。
        • 我会,但是我的日期格式很奇怪,月份在前,毫秒部分用第三个冒号而不是点分隔。无论如何,我认为将这种方法写在某个地方仍然很有用,因为大多数来源要么使用推理,要么使用 date_parser,没有给出 date_format 的快速替代方案。 IIRC 文档对于如何将多个日期列作为数组传递给 parse_dates 时如何组合也含糊不清(它们用空格分隔)。
        【解决方案9】:

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

        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 can be dropped 并用空格代替)你应该指定infer_datetime_format(其中显然与更常见的解析器没有区别)并且传递您自己的解析器只会削弱性能。另一方面,date_parser 确实与不那么标准的日期格式有所不同。像往常一样,请务必在优化之前安排好时间。

        【讨论】:

          【解决方案10】:

          您可以将参数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'])
          

          【讨论】:

            【解决方案11】:

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

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

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

            【讨论】:

              猜你喜欢
              • 2023-03-24
              • 2023-02-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-27
              • 2018-02-28
              • 1970-01-01
              相关资源
              最近更新 更多