Определите, какие столбцы даты и времени

14

У меня огромный массив данных со многими столбцами, многие из которых имеют тип 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 тех столбцов, значения которых действительно содержат datetimeзначения, но также могут содержать Noneи, возможно, некоторые недопустимые значения (упомяну, так как в противном случае в выражении a pd.to_datetimeв выражении try/ except)? Что-то вроде гибкой версииpd.to_datetime(col)

Yatu
источник
Объект хранится в типе DataFrame datetime.datetimeили pandas._libs.tslibs.timestamps.Timestamp? В первом случае я бы рекомендовал изменить созданный тип datetime на тип, который pandasлучше обрабатывает.
ALollz
Есть ли Noneв ваших столбцах фактические Noneили строковые представители этого?
Эрфан
Они Noneне строки. Потенциально могут быть неправильные значения также ... @erfan
Yatu
3
Тогда мне интересно, как модель sql в вашей базе данных? Поскольку sql форсирует определенные типы столбцов. Как вы попали в столбцы смешанного типа? Может быть, вы также можете показать столбец, который имеет datetimeи valuesв нем?
Эрфан
1
используйте парсер dateutil, чтобы угадать datetime. Может быть установлен порог в несколько (скажем, 5 дат) в столбце, чтобы быть уверенным в stackoverflow.com/questions/9507648/…
Serge

Ответы:

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
piRSquared
источник
Ну, похоже, это очень сильно упрощает проблему. Я даже не думал об этом. Идеальным сценарием было просто применить pd.to_datetimeи coerceошибок, так как их много. Проблема была с числовыми столбцами. Но мне не пришло в голову, что числовые столбцы, приведенные к строке, не анализируются пандами » to_datetime. Большое спасибо, это действительно помогает!
Yatu
4

Эта функция устанавливает тип данных столбца в datetime, если какое-либо значение в столбце соответствует шаблону регулярного выражения (\ d {4} - \ d {2} - \ d {2}) + (например, 2019-01-01 ). Отдайте должное этому ответу о том, как искать строку во всех столбцах 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, это может помочь. Он все еще работает, исходя из предположения, что если в столбце есть какие-либо значения типа даты, то столбец должен быть датой-временем. Я попытался рассмотреть различные методы итераций для фреймов данных, которые быстрее. Я думаю, что этот ответ о том, как перебирать строки в DataFrame в Pandas , хорошо их описал.

Обратите внимание, что dateutil.parserбудет использоваться текущий день или год для любых строк, таких как «декабрь» или «ноябрь 2019» без значений года или дня.

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 from dateutil.parser, вы можете добавить это:

for col in list_of_datetime_columns:
    df[col] = df[col].apply(lambda x: parse_dates(x))
Да это Рик
источник
Это хорошая идея, но, к сожалению, я ищу что-то, что может обобщать потенциально несколько различных форматов даты и времени, поэтому без жесткого кодирования формата. Цените усилия , хотя
Yatu
@yatu Не проблема - я просто работал над чем-то, что нуждалось в этом. Интересно, можете ли вы обобщить все форматы даты и времени? Возможно, вам придется заранее учитывать все форматы, которые вы ожидаете увидеть; или все форматы, которые вы считаете допустимыми.
Да, это Рик
@yatu На самом деле этот dateutilмодуль, упомянутый @Serge, выглядит очень полезным.
Да, это Рик
@yatu, пожалуйста, смотрите мой обновленный ответ. Я использовал, dateutil.parseчтобы идентифицировать много различных типов строк даты.
Да, это Рик
Выглядит хорошо! не так много времени , теперь рассмотрим , как только я могу @yes
Yatu