Schliessen Sie, welche Spalten datetime sind

14

Ich habe einen riesigen Datenrahmen mit vielen Spalten, von denen viele vom Typ sind datetime.datetime. Das Problem ist, dass viele auch gemischte Typen haben, einschließlich zum Beispiel datetime.datetimeWerte und NoneWerte (und möglicherweise andere ungültige Werte):

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

Daraus ergibt sich eine objectTypenspalte. Dies kann mit gelöst werden df.colx.fillna(pd.NaT). Das Problem ist, dass der Datenrahmen zu groß ist, um nach einzelnen Spalten zu suchen.

Ein anderer Ansatz ist die Verwendung pd.to_datetime(col, errors='coerce'), dies wird jedoch in datetimeviele Spalten umgewandelt, die numerische Werte enthalten.

Ich könnte es auch tun df.fillna(float('nan'), inplace=True), obwohl die Spalten, die Datumsangaben enthalten, immer noch vom objectTyp sind und immer noch das gleiche Problem haben würden.

Welchen Ansatz könnte ich verfolgen, um die Spalten, deren Werte tatsächlich Werte enthalten datetime, aber auch Noneund möglicherweise einige ungültige Werte enthalten könnten, auf datetime umzuwandeln (da ansonsten a pd.to_datetimein a erwähnt wird)try / except-Klausel dies tun würde)? So etwas wie eine flexible Version vonpd.to_datetime(col)

Yatu
quelle
Sind die Objekte im DataFrame-Typ gespeichert datetime.datetimeoder pandas._libs.tslibs.timestamps.Timestamp? In diesem Fall würde ich empfehlen, die Datums- und Uhrzeitangabe auf den Typ zu ändern, der pandasetwas besser funktioniert .
ALollz
Sind die Nonein Ihren Spalten tatsächlichen Noneoder Zeichenfolgenvertreter davon?
Erfan
Sie sind Nonekeine Schnur. Möglicherweise kann es auch falsche Werte geben ... @erfan
yatu
3
Dann frage ich mich, wie ist das SQL-Modell in Ihrer Datenbank? Da erzwingt SQL bestimmte Arten von Spalten. Wie sind Sie zu gemischten Spalten gekommen? Können Sie vielleicht auch eine Spalte anzeigen, die datetimeund valuesdarin enthalten ist?
Erfan
1
Verwenden Sie den Datums-Parser, um die Uhrzeit zu erraten. Kann
Serge

Antworten:

1

Das Hauptproblem, das ich sehe, ist das Parsen von numerischen Werten.

Ich würde vorschlagen, sie zuerst in Zeichenfolgen umzuwandeln


Installieren

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

Lösung

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
quelle
Nun, es sieht so aus, als würde dies das Problem nur enorm vereinfachen. Daran habe ich gar nicht gedacht. Das ideale Szenario war, einfach anzuwenden pd.to_datetimeund coercedie Fehler, da es viele gibt. Das Problem war mit den numerischen Spalten. Mir ist aber nicht in den Sinn gekommen, dass numerische Spalten, die in Zeichenfolgen umgewandelt werden, nicht von Pandas analysiert werden to_datetime. Vielen Dank, das hilft wirklich!
Yatu
4

Diese Funktion setzt den Datentyp einer Spalte auf datetime, wenn ein Wert in der Spalte mit dem Regex-Muster (\ d {4} - \ d {2} - \ d {2}) + übereinstimmt (z. B. 01.01.2019) ). Wir danken dieser Antwort für die Suche nach Zeichenfolgen in allen Pandas DataFrame-Spalten und -Filtern , die beim Festlegen und Anwenden der Maske hilfreich waren.

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

Arbeiten aus dem Vorschlag zu verwenden dateutil kann dies hilfreich sein. Es wird immer noch davon ausgegangen, dass die Spalte eine datetime sein sollte, wenn in einer Spalte datumsähnliche Werte vorhanden sind. Ich habe versucht, verschiedene Datenrahmen-Iterationsmethoden in Betracht zu ziehen, die schneller sind. Ich denke, diese Antwort zum Durchlaufen von Zeilen in einem DataFrame in Pandas hat gute Arbeit geleistet, um sie zu beschreiben.

Beachten Sie, dass dateutil.parserder aktuelle Tag oder das aktuelle Jahr für Zeichenfolgen wie "Dezember" oder "November 2019" ohne Jahres- oder Tageswerte verwendet wird.

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]

Wenn Sie auch die Datenzeitwerte von verwenden möchten dateutil.parser, können Sie Folgendes hinzufügen:

for col in list_of_datetime_columns:
    df[col] = df[col].apply(lambda x: parse_dates(x))
Ja das ist Rick
quelle
Dies ist eine nette Idee, aber leider suche ich nach etwas, das sich auf möglicherweise mehrere verschiedene Datums- / Uhrzeitformate verallgemeinern lässt, ohne das Format fest zu codieren.
Schätzen Sie
@yatu Kein Problem - ich habe gerade an etwas gearbeitet, das dies brauchte. Ich frage mich, ob Sie auf alle Datums- / Uhrzeitformate verallgemeinern können. Möglicherweise müssen Sie alle Formate, die Sie erwarten würden, im Voraus berücksichtigen. oder alle Formate, die Sie als gültige Datums- / Uhrzeitangabe betrachten würden.
Ja, das ist Rick
@yatu Eigentlich dateutilsieht das von @Serge erwähnte Modul so aus, als könnte es nützlich sein.
Ja, das ist Rick
@yatu siehe meine aktualisierte Antwort. Ich habe dateutil.parseviele verschiedene Arten von Datumszeichenfolgen identifiziert.
Ja, das ist Rick
Sieht gut aus! Habe jetzt nicht viel Zeit, werde einen Blick darauf werfen, sobald ich kann @yes
yatu