Suy ra cột nào là datetime


14

Tôi có một khung dữ liệu khổng lồ với nhiều cột, nhiều cột có kiểu datetime.datetime. Vấn đề là nhiều người cũng có các loại hỗn hợp, bao gồm các datetime.datetimegiá trị và giá trị ví dụ None(và có thể có các giá trị không hợp lệ khác):

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

Do đó dẫn đến một objectcột loại. Điều này có thể được giải quyết với df.colx.fillna(pd.NaT). Vấn đề là khung dữ liệu quá lớn để tìm kiếm các cột riêng lẻ.

Một cách tiếp cận khác là sử dụng pd.to_datetime(col, errors='coerce'), tuy nhiên điều này sẽ truyền tới datetimenhiều cột có chứa các giá trị số.

Tôi cũng có thể làm df.fillna(float('nan'), inplace=True), mặc dù các cột chứa ngày vẫn thuộc objectloại và vẫn có cùng một vấn đề.

Cách tiếp cận nào tôi có thể làm theo để truyền tới datetime những cột có giá trị thực sự chứa datetimegiá trị, nhưng cũng có thể chứa Nonevà có khả năng một số giá trị không hợp lệ (đề cập đến nếu không thì pd.to_datetimetrong mệnh đề try/ exceptsẽ làm gì)? Một cái gì đó giống như một phiên bản linh hoạt củapd.to_datetime(col)


Là đối tượng được lưu trữ trong loại DataFrame datetime.datetimehay pandas._libs.tslibs.timestamps.Timestamp? Nếu trước đây khuyến nghị của tôi sẽ là thay đổi bất cứ điều gì đã tạo ra datetime thành loại pandasxử lý tốt hơn một chút.
ALollz

Nonetrong các cột của bạn, Noneđại diện thực tế hoặc chuỗi của nó?
Erfan

Họ là None, không phải chuỗi. Có khả năng cũng có thể có các giá trị sai ... @erfan
yatu

3
Sau đó tôi tự hỏi, mô hình sql trong cơ sở dữ liệu của bạn như thế nào? Kể từ khi sql buộc một số loại cột. Làm thế nào bạn kết thúc với các loại cột hỗn hợp? Bạn cũng có thể hiển thị một cột có datetimevaluestrong đó?
Erfan

1
sử dụng trình phân tích cú pháp dateutil để đoán datetime. Có thể đặt ngưỡng của một số (giả sử 5 ngày) trong cột để chắc chắn stackoverflow.com/questions/9507648/ Kẻ
Serge

Câu trả lời:


1

Vấn đề chính tôi thấy là khi phân tích các giá trị số.

Tôi đề nghị chuyển đổi chúng thành chuỗi trước


Thiết lập

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

Giải pháp

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

Vâng, có vẻ như điều này chỉ đơn giản hóa rất nhiều vấn đề. Tôi thậm chí không nghĩ về điều này. Kịch bản lý tưởng là chỉ cần áp dụng pd.to_datetimecoercecác lỗi, sInce có rất nhiều. Vấn đề là với các cột số. Nhưng điều đó không xảy ra với tôi rằng các cột số được đúc thành chuỗi không được phân tích cú pháp bởi gấu trúc ' to_datetime. Cảm ơn rất nhiều, điều này thực sự có ích!
yatu

4

Hàm này sẽ đặt kiểu dữ liệu của cột thành datetime, nếu bất kỳ giá trị nào trong cột khớp với mẫu biểu thức chính quy (\ d {4} - \ d {2} - \ d {2}) + (ví dụ: 2019-01-01 ). Tín dụng cho câu trả lời này về cách Tìm kiếm Chuỗi trong tất cả các cột và bộ lọc DataFrame của Pandas đã giúp cài đặt và áp dụng mặt nạ.

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

Làm việc từ gợi ý để sử dụng dateutil, điều này có thể giúp đỡ. Nó vẫn đang làm việc với giả định rằng nếu có bất kỳ giá trị giống như ngày nào trong một cột, thì cột đó phải là một mốc thời gian. Tôi đã cố gắng xem xét các phương pháp lặp dữ liệu khác nhau nhanh hơn. Tôi nghĩ câu trả lời này về Cách lặp qua các hàng trong DataFrame ở Pandas đã làm rất tốt khi mô tả chúng.

Lưu ý rằng dateutil.parsersẽ sử dụng ngày hoặc năm hiện tại cho bất kỳ chuỗi nào như 'Tháng 12' hoặc 'Tháng 11 năm 2019' không có giá trị năm hoặc ngày.

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]

Trong trường hợp bạn cũng muốn sử dụng các giá trị datatime từ dateutil.parser, bạn có thể thêm điều này:

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

Đây là một ý tưởng hay, nhưng thật không may, tôi đang tìm kiếm thứ gì đó có thể khái quát hóa cho nhiều định dạng datetime khác nhau, do đó không mã hóa định dạng. Đánh giá cao những nỗ lực mặc dù
yatu

@yatu Không phải là vấn đề - Tôi chỉ tình cờ làm việc về thứ gì đó cần cái này. Tôi tự hỏi nếu bạn có thể khái quát cho tất cả các định dạng datetime? Bạn có thể phải tài khoản trước thời hạn cho tất cả các định dạng mà bạn muốn thấy; hoặc, tất cả các định dạng bạn sẽ coi là datetime hợp lệ.
Vâng, đây là Rick

@yatu Trên thực tế, dateutilmô-đun được đề cập bởi @Serge có vẻ như nó có thể hữu ích.
Vâng, đây là Rick

@yatu vui lòng xem câu trả lời cập nhật của tôi. Tôi đã sử dụng dateutil.parseđể xác định nhiều loại chuỗi ngày khác nhau.
Vâng, đây là Rick

Có vẻ tốt! bây giờ không có nhiều thời gian, hãy xem ngay khi tôi có thể @yes
yatu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.