2016-07-26 1 views
1

У меня есть кадр данных pandas с несколькими столбцами строк, представляющих даты, с пустыми строками, представляющими отсутствующие даты. Напримерpandas конвертировать строки столбцов в datetime, что позволяет пропустить, но не является недопустимым

import numpy as np 
import pandas as pd 

# expected date format is 'm/%d/%Y' 

custId = np.array(list(range(1,6))) 
eventDate = np.array(["06/10/1992","08/24/2012","04/24/2015","","10/14/2009"]) 
registerDate = np.array(["06/08/2002","08/20/2012","04/20/2015","","10/10/2009"]) 

# both date columns of dfGood should convert to datetime without error 
dfGood = pd.DataFrame({'custId':custId, 'eventDate':eventDate, 'registerDate':registerDate}) 

Я пытаюсь:

  • эффективно преобразовать столбцы, где все строки являются допустимой датой или пустыми в столбцы типа datetime64NaT для пустого)
  • Поднять ValueError когда-либо непустая строка не соответствует ожидаемому формату,

Пример того, где ValueError должны быть подняты:

# 2nd string invalid 
registerDate = np.array(["06/08/2002","20/08/2012","04/20/2015","","10/10/2009"]) 
# eventDate column should convert, registerDate column should raise ValueError 
dfBad = pd.DataFrame({'custId':custId, 'eventDate':eventDate, 'registerDate':registerDate}) 

Эта функция делает то, что я хочу на уровне элемента:

from datetime import datetime 

def parseStrToDt(s, format = '%m/%d/%Y'): 
    """Parse a string to datetime with the supplied format.""" 
    return pd.NaT if s=='' else datetime.strptime(s, format) 

print(parseStrToDt("")) # correctly returns NaT 
print(parseStrToDt("12/31/2011")) # correctly returns 2011-12-31 00:00:00 
print(parseStrToDt("12/31/11")) # correctly raises ValueError 

Однако, у меня есть read что строковые операции не должны быть np.vectorize -d. Я думал, что это может быть сделано эффективно, используя pandas.DataFrame.apply, как:

dfGood[['eventDate','registerDate']].applymap(lambda s: parseStrToDt(s)) # raises TypeError 

dfGood.loc[:,'eventDate'].apply(lambda s: parseStrToDt(s)) # raises same TypeError 

Я предполагаю, что TypeError что-то делать с моей функцией возврата различных dtype, но я хочу, чтобы воспользоваться динамической типизации и замените строку на datetime (если ValueError не поднимается) ... так как я могу это сделать?

+0

Вы можете просто использовать 'pd.to_datetime' с параметром' errors = 'coerce'' так 'pd.to_datetime (x, errors =' coerce ')' где 'x' является вашим столбцом df – EdChum

+0

@EdChum спасибо, но' pd.to_datetime (dfBad ['registerDate'], errors = 'coerce') 'не поднимает' ValueError', и я ищу, чтобы поднять 'ValueError' на недопустимые строки даты. Установка 'errors = 'coerce'' предотвращает это. – C8H10N4O2

+0

Но дело в том, что вы получите 'np.NaT' (не время) для недопустимых или пустых строк, и вы можете отфильтровать их с помощью' dropna' – EdChum

ответ

1

pandas не имеет возможности точно повторять то, что вы хотите, вот один из способов сделать это, что должно быть относительно эффективным.

In [4]: dfBad 
Out[4]: 
    custId eventDate registerDate 
0  1 06/10/1992 06/08/2002 
1  2 08/24/2012 20/08/2012 
2  3 04/24/2015 04/20/2015 
3  4       
4  5 10/14/2009 10/10/2009 

In [7]: cols 
Out[7]: ['eventDate', 'registerDate'] 

In [9]: dts = dfBad[cols].apply(lambda x: pd.to_datetime(x, errors='coerce', format='%m/%d/%Y')) 

In [10]: dts 
Out[10]: 
    eventDate registerDate 
0 1992-06-10 2002-06-08 
1 2012-08-24   NaT 
2 2015-04-24 2015-04-20 
3  NaT   NaT 
4 2009-10-14 2009-10-10 

In [11]: mask = pd.isnull(dts) & (dfBad[cols] != '') 

In [12]: mask 
Out[12]: 
    eventDate registerDate 
0  False  False 
1  False   True 
2  False  False 
3  False  False 
4  False  False 


In [13]: mask.any() 
Out[13]: 
eventDate  False 
registerDate  True 
dtype: bool 

In [14]: is_bad = mask.any() 

In [23]: if is_bad.any(): 
    ...:  raise ValueError("bad dates in col(s) {0}".format(is_bad[is_bad].index.tolist())) 
    ...: else: 
    ...:  df[cols] = dts 
    ...:  
--------------------------------------------------------------------------- 
ValueError        Traceback (most recent call last) 
<ipython-input-23-579c06ce3c77> in <module>() 
     1 if is_bad.any(): 
----> 2  raise ValueError("bad dates in col(s) {0}".format(is_bad[is_bad].index.tolist())) 
     3 else: 
     4  df[cols] = dts 
     5 

ValueError: bad dates in col(s) ['registerDate'] 
+0

Ницца, спасибо. Необходимо еще один шаг, который должен идентифицировать хорошие столбцы и преобразовать их, оставляя только плохие столбцы с исключением (которые будут обрабатываться в другом месте), но я возьму это как упражнение. – C8H10N4O2

1

Просто взять принятый ответ немного дальше, я заменил столбцы всех действительных или отсутствующих строк с их разобранные DateTimes, а затем поднял ошибку для остальных неразбираемых столбцов:

dtCols = ['eventDate', 'registerDate'] 
dts = dfBad[dtCols].apply(lambda x: pd.to_datetime(x, errors='coerce', format='%m/%d/%Y')) 

mask = pd.isnull(dts) & (dfBad[dtCols] != '') 
colHasError = mask.any() 

invalidCols = colHasError[colHasError].index.tolist() 
validCols = list(set(dtCols) - set(invalidCols)) 

dfBad[validCols] = dts[validCols] # replace the completely valid/empty string cols with dates 
if colHasError.any(): 
    raise ValueError("bad dates in col(s) {0}".format(invalidCols)) 
# raises: ValueError: bad dates in col(s) ['registerDate'] 

print(dfBad) # eventDate got converted, registerDate didn't 

Принятый ответ содержит основное понимание, которое должно идти вперед и принуждать ошибки до NaT, а затем различать непустые, но недействительные строки из пустых с помощью маски.

Смежные вопросы