อนุมานว่าคอลัมน์ใดเป็น datetime


14

ฉันมี dataframe 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)นี้สามารถแก้ไขได้ด้วย ปัญหาคือว่า dataframe ใหญ่เกินกว่าจะค้นหาแต่ละคอลัมน์ได้

อีกวิธีคือการใช้pd.to_datetime(col, errors='coerce')แต่วิธีนี้จะส่งไปยังdatetimeคอลัมน์จำนวนมากที่มีค่าตัวเลข

ฉันสามารถทำได้df.fillna(float('nan'), inplace=True)แม้ว่าคอลัมน์ที่มีวันที่ยังคงเป็นobjectประเภทและจะยังคงมีปัญหาเดียวกัน

สิ่งที่วิธีที่ฉันสามารถปฏิบัติตามเพื่อโยน datetime คอลัมน์เหล่านั้นมีค่าจริงๆมีdatetimeค่า แต่ยังอาจมีNoneและอาจบางค่าไม่ถูกต้อง (กล่าวขวัญตั้งแต่อื่นpd.to_datetimeในtry/ exceptข้อจะทำ)? บางอย่างเช่นเวอร์ชันที่ยืดหยุ่นของpd.to_datetime(col)


มีการจัดเก็บวัตถุในประเภท DataFrame datetime.datetimeหรือpandas._libs.tslibs.timestamps.Timestampไม่ หากคำแนะนำเดิมของฉันจะเปลี่ยนสิ่งที่สร้างวันที่และเวลาเป็นประเภทที่pandasจัดการได้ดีขึ้นเล็กน้อย
ALollz

เป็นNoneในคอลัมน์ของคุณที่เกิดขึ้นจริงNoneแทนหรือสตริงของมันได้หรือไม่
Erfan

มันNoneไม่ใช่สตริง อาจเป็นไปได้ว่าอาจมีค่าผิด ๆ ด้วย ... @erfan
yatu

3
ถ้าอย่างนั้นฉันสงสัยว่า sql model ในฐานข้อมูลของคุณเป็นอย่างไร? เนื่องจาก sql บังคับคอลัมน์บางประเภท คุณจบลงด้วยคอลัมน์ประเภทต่าง ๆ ได้อย่างไร คุณสามารถแสดงคอลัมน์ที่มีdatetimeและvaluesอยู่ด้วยได้ไหม
Erfan

1
ใช้ตัวแยกวิเคราะห์ dateutil เพื่อเดาวันที่และเวลา อาจจะเป็นชุดของเกณฑ์หลายคน (พูด 5 วัน) ในคอลัมน์เพื่อให้แน่ใจว่าstackoverflow.com/questions/9507648/...
เสิร์จ

คำตอบ:


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_datetimeและcoerceข้อผิดพลาดเนื่องจากมีหลายอย่าง ปัญหาเกิดขึ้นกับคอลัมน์ตัวเลข แต่มันไม่ได้เกิดขึ้นกับผมว่าคอลัมน์ตัวเลขโยนไปสตริงยังไม่ได้แยกวิเคราะห์โดยนุ่น to_datetimeขอบคุณมากนี่ช่วยได้จริงๆ!
yatu

4

ฟังก์ชันนี้จะตั้งค่าชนิดข้อมูลของคอลัมน์เป็น datetime หากค่าใด ๆ ในคอลัมน์ตรงกับรูปแบบ regex (\ 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]

ในกรณีที่คุณต้องการใช้ค่าข้อมูลจากdateutil.parserคุณสามารถเพิ่ม:

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

นี่เป็นความคิดที่ดี แต่น่าเสียดายที่ฉันกำลังมองหาบางอย่างที่สามารถพูดคุยกับรูปแบบวันที่และเวลาที่แตกต่างกันได้หลายรูปแบบดังนั้นจึงไม่มีการเข้ารหัสรูปแบบ ขอบคุณสำหรับความพยายาม
yatu

@yatu ไม่มีปัญหา - ฉันเพิ่งเกิดขึ้นที่จะทำงานในสิ่งที่ต้องการนี้ ฉันสงสัยว่าคุณสามารถคุยกับทุกรูปแบบวันที่และเวลาได้หรือไม่? คุณอาจต้องคิดล่วงหน้าสำหรับรูปแบบทั้งหมดที่คุณคาดว่าจะเห็น หรือรูปแบบทั้งหมดที่คุณพิจารณาว่าเป็น datetime ที่ถูกต้อง
ใช่นี่คือ Rick

@yatu จริงๆแล้วdateutilโมดูลที่กล่าวถึงโดย @Serge ดูเหมือนว่ามันจะมีประโยชน์
ใช่นี่คือริก

@yatu โปรดดูคำตอบที่อัปเดตของฉัน ฉันใช้dateutil.parseเพื่อระบุสตริงวันที่หลายประเภท
ใช่นี่คือริก

ดูดี! ไม่มีเวลามากจะดูทันทีที่ฉันสามารถ @yes
yatu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.