推断哪些列是日期时间


14

我有一个巨大的数据框,其中包含许多列,其中许多列都是type的datetime.datetime。问题在于,许多还具有混合类型,包括例如datetime.datetime值和None值(以及可能的其他无效值):

0         2017-07-06 00:00:00
1         2018-02-27 21:30:05
2         2017-04-12 00:00:00
3         2017-05-21 22:05:00
4         2018-01-22 00:00:00
                 ...         
352867    2019-10-04 00:00:00
352868                   None
352869            some_string
Name: colx, Length: 352872, dtype: object

因此导致object类型列。可以用解决df.colx.fillna(pd.NaT)。问题在于数据框太大,无法搜索单个列。

另一种方法是使用pd.to_datetime(col, errors='coerce'),但是这将强制转换为datetime包含数值的许多列。

我也可以做df.fillna(float('nan'), inplace=True),尽管包含日期的列仍然是object类型,并且仍然会有相同的问题。

我可以采用什么方法将那些其值确实包含datetime值但也可能包含None,以及可能包含一些无效值的列转换为日期时间(提及,因为否则pd.to_datetimetry/ except子句中可以执行)?像是弹性版本pd.to_datetime(col)


存储在数据帧类型的对象datetime.datetimepandas._libs.tslibs.timestamps.Timestamp?如果是前者,我的建议是将创建日期时间的任何内容更改为pandas处理起来更好的类型。
ALollz

None您的专栏文章中的,实际的None还是字符串的代表?
Erfan

它们是None,不是字符串。潜在地也可能有错误的值... @erfan
yatu

3
然后我想知道,数据库中的sql模型如何?由于sql强制使用某些类型的列。您如何最终得到混合类型的列?您能否也显示包含datetime和的列values
Erfan

1
使用dateutil解析器猜测日期时间。可以在列中设置几个阈值(例如5个日期),以确保stackoverflow.com/questions/9507648/…–
Serge

Answers:


1

我看到的主要问题是解析数值时。

我建议先将它们转换为字符串


设定

dat = {
    'index': [0, 1, 2, 3, 4, 352867, 352868, 352869],
    'columns': ['Mixed', 'Numeric Values', 'Strings'],
    'data': [
        ['2017-07-06 00:00:00', 1, 'HI'],
        ['2018-02-27 21:30:05', 1, 'HI'],
        ['2017-04-12 00:00:00', 1, 'HI'],
        ['2017-05-21 22:05:00', 1, 'HI'],
        ['2018-01-22 00:00:00', 1, 'HI'],
        ['2019-10-04 00:00:00', 1, 'HI'],
        ['None', 1, 'HI'],
        ['some_string', 1, 'HI']
    ]
}

df = pd.DataFrame(**dat)

df

                      Mixed  Numeric Values Strings
0       2017-07-06 00:00:00               1      HI
1       2018-02-27 21:30:05               1      HI
2       2017-04-12 00:00:00               1      HI
3       2017-05-21 22:05:00               1      HI
4       2018-01-22 00:00:00               1      HI
352867  2019-10-04 00:00:00               1      HI
352868                 None               1      HI
352869          some_string               1      HI

df.astype(str).apply(pd.to_datetime, errors='coerce')

                     Mixed Numeric Values Strings
0      2017-07-06 00:00:00            NaT     NaT
1      2018-02-27 21:30:05            NaT     NaT
2      2017-04-12 00:00:00            NaT     NaT
3      2017-05-21 22:05:00            NaT     NaT
4      2018-01-22 00:00:00            NaT     NaT
352867 2019-10-04 00:00:00            NaT     NaT
352868                 NaT            NaT     NaT
352869                 NaT            NaT     NaT

好吧,看起来这极大地简化了问题。我什至没有想到这一点。理想的情况是简单地应用pd.to_datetimecoerce错误,因为还有很多。问题出在数字列上。但是我没有想到pandas不能解析转换为字符串的数字列to_datetime。非常感谢,这确实有帮助!
yatu

4

如果该列中的任何值与正则表达式模式(\ d {4}-\ d {2}-\ d {2})+(例如2019-01-01)相匹配,则此函数会将列的数据类型设置为datetime。 )。这个答案归功于如何在所有Pandas DataFrame列中搜索字符串以及如何帮助设置和应用遮罩的过滤器

def presume_date(dataframe):
    """ Set datetime by presuming any date values in the column
        indicates that the column data type should be datetime.

    Args:
        dataframe: Pandas dataframe.

    Returns:
        Pandas dataframe.

    Raises:
        None
    """
    df = dataframe.copy()
    mask = dataframe.astype(str).apply(lambda x: x.str.match(
        r'(\d{4}-\d{2}-\d{2})+').any())
    df_dates = df.loc[:, mask].apply(pd.to_datetime, errors='coerce')
    for col in df_dates.columns:
        df[col] = df_dates[col]
    return df

从建议使用到使用dateutil,这可能会有所帮助。仍然在假设列中是否存在任何类似日期的值,该列应为日期时间。我试图考虑更快的其他数据框迭代方法。我认为有关如何迭代Pandas中DataFrame中的行的答案很好地描述了它们。

请注意,dateutil.parser对于任何没有年份或日期值的字符串,例如``十二月''或``2019年11月'' ,它将使用当前日期或年份。

import pandas as pd
import datetime
from dateutil.parser import parse

df = pd.DataFrame(columns=['are_you_a_date','no_dates_here'])
df = df.append(pd.Series({'are_you_a_date':'December 2015','no_dates_here':'just a string'}), ignore_index=True)
df = df.append(pd.Series({'are_you_a_date':'February 27 2018','no_dates_here':'just a string'}), ignore_index=True)
df = df.append(pd.Series({'are_you_a_date':'May 2017 12','no_dates_here':'just a string'}), ignore_index=True)
df = df.append(pd.Series({'are_you_a_date':'2017-05-21','no_dates_here':'just a string'}), ignore_index=True)
df = df.append(pd.Series({'are_you_a_date':None,'no_dates_here':'just a string'}), ignore_index=True)
df = df.append(pd.Series({'are_you_a_date':'some_string','no_dates_here':'just a string'}), ignore_index=True)
df = df.append(pd.Series({'are_you_a_date':'Processed: 2019/01/25','no_dates_here':'just a string'}), ignore_index=True)
df = df.append(pd.Series({'are_you_a_date':'December','no_dates_here':'just a string'}), ignore_index=True)


def parse_dates(x):
    try:
        return parse(x,fuzzy=True)
    except ValueError:
        return ''
    except TypeError:
        return ''


list_of_datetime_columns = []
for row in df:
    if any([isinstance(parse_dates(row[0]),
                       datetime.datetime) for row in df[[row]].values]):
        list_of_datetime_columns.append(row)

df_dates = df.loc[:, list_of_datetime_columns].apply(pd.to_datetime, errors='coerce')

for col in list_of_datetime_columns:
    df[col] = df_dates[col]

如果您还想使用中的datatime值dateutil.parser,则可以添加以下内容:

for col in list_of_datetime_columns:
    df[col] = df[col].apply(lambda x: parse_dates(x))

这是一个不错的主意,但不幸的是,我正在寻找可以推广到可能具有几种不同日期时间格式的东西,因此无需对格式进行硬编码。虽然赞赏努力
yatu

@yatu没问题-我刚巧正在做一些需要这个的事情。我想知道您是否可以泛化为所有日期时间格式?您可能需要提前考虑所有您期望看到的格式;或所有您认为有效的日期时间格式。
是的,这是里克(Rick)

@yatu实际上,@ Serge dateutil提到的模块看起来可能有用。
是的,这是里克(Rick)

@yatu,请参阅我的最新答案。我曾经dateutil.parse识别许多不同类型的日期字符串。
是的,这是里克

看起来不错!现在没有很多时间,请尽快查看@yes
yatu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.