11from datetime import datetime , timedelta , time
22import numpy as np
3+ from collections import MutableMapping
34
45import pandas .lib as lib
56import pandas .tslib as tslib
67import pandas .core .common as com
7- from pandas .core .common import ABCIndexClass
8+ from pandas .core .common import ABCIndexClass , ABCSeries , ABCDataFrame
89import pandas .compat as compat
910from pandas .util .decorators import deprecate_kwarg
1011
@@ -175,7 +176,12 @@ def to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False,
175176
176177 Parameters
177178 ----------
178- arg : string, datetime, list, tuple, 1-d array, or Series
179+ arg : string, datetime, list, tuple, 1-d array, Series
180+
181+ .. versionadded: 0.18.1
182+
183+ or DataFrame/dict-like
184+
179185 errors : {'ignore', 'raise', 'coerce'}, default 'raise'
180186
181187 - If 'raise', then invalid parsing will raise an exception
@@ -282,6 +288,18 @@ def to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False,
282288 >>> pd.to_datetime('13000101', format='%Y%m%d', errors='coerce')
283289 NaT
284290
291+
292+ Assembling a datetime from multiple columns of a DataFrame. The keys can be
293+ strplike (%Y, %m) or common abbreviations like ('year', 'month')
294+
295+ >>> df = pd.DataFrame({'year': [2015, 2016],
296+ 'month': [2, 3],
297+ 'day': [4, 5]})
298+ >>> pd.to_datetime(df)
299+ 0 2015-02-04
300+ 1 2016-03-05
301+ dtype: datetime64[ns]
302+
285303 """
286304 return _to_datetime (arg , errors = errors , dayfirst = dayfirst ,
287305 yearfirst = yearfirst ,
@@ -296,7 +314,6 @@ def _to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False,
296314 Same as to_datetime, but accept freq for
297315 DatetimeIndex internal construction
298316 """
299- from pandas .core .series import Series
300317 from pandas .tseries .index import DatetimeIndex
301318
302319 def _convert_listlike (arg , box , format , name = None ):
@@ -407,16 +424,136 @@ def _convert_listlike(arg, box, format, name=None):
407424 return arg
408425 elif isinstance (arg , tslib .Timestamp ):
409426 return arg
410- elif isinstance (arg , Series ):
427+ elif isinstance (arg , ABCSeries ):
428+ from pandas import Series
411429 values = _convert_listlike (arg ._values , False , format )
412430 return Series (values , index = arg .index , name = arg .name )
431+ elif isinstance (arg , (ABCDataFrame , MutableMapping )):
432+ return _assemble_from_unit_mappings (arg , errors = errors )
413433 elif isinstance (arg , ABCIndexClass ):
414434 return _convert_listlike (arg , box , format , name = arg .name )
415435 elif com .is_list_like (arg ):
416436 return _convert_listlike (arg , box , format )
417437
418438 return _convert_listlike (np .array ([arg ]), box , format )[0 ]
419439
440+ # mappings for assembling units
441+ _unit_map = {'year' : 'year' ,
442+ 'y' : 'year' ,
443+ '%Y' : 'year' ,
444+ 'month' : 'month' ,
445+ 'M' : 'month' ,
446+ '%m' : 'month' ,
447+ 'day' : 'day' ,
448+ 'days' : 'day' ,
449+ 'd' : 'day' ,
450+ '%d' : 'day' ,
451+ 'h' : 'h' ,
452+ 'hour' : 'h' ,
453+ 'hh' : 'h' ,
454+ '%H' : 'h' ,
455+ 'minute' : 'm' ,
456+ 't' : 'm' ,
457+ 'min' : 'm' ,
458+ '%M' : 'm' ,
459+ 'mm' : 'm' ,
460+ 'MM' : 'm' ,
461+ '%M' : 'm' ,
462+ 's' : 's' ,
463+ 'seconds' : 's' ,
464+ 'second' : 's' ,
465+ '%S' : 's' ,
466+ 'ss' : 's' ,
467+ 'ms' : 'ms' ,
468+ 'millisecond' : 'ms' ,
469+ 'milliseconds' : 'ms' ,
470+ 'us' : 'us' ,
471+ 'microsecond' : 'us' ,
472+ 'microseconds' : 'us' ,
473+ 'ns' : 'ns' ,
474+ 'nanosecond' : 'ns' ,
475+ 'nanoseconds' : 'ns'
476+ }
477+
478+
479+ def _assemble_from_unit_mappings (arg , errors ):
480+ """
481+ assemble the unit specifed fields from the arg (DataFrame)
482+ Return a Series for actual parsing
483+
484+ Parameters
485+ ----------
486+ arg : DataFrame
487+ errors : {'ignore', 'raise', 'coerce'}, default 'raise'
488+
489+ - If 'raise', then invalid parsing will raise an exception
490+ - If 'coerce', then invalid parsing will be set as NaT
491+ - If 'ignore', then invalid parsing will return the input
492+
493+ Returns
494+ -------
495+ Series
496+ """
497+ from pandas import to_timedelta , to_numeric , DataFrame
498+ arg = DataFrame (arg )
499+ if not arg .columns .is_unique :
500+ raise ValueError ("cannot assemble with duplicate keys" )
501+
502+ # replace passed unit with _unit_map
503+ def f (value ):
504+ if value in _unit_map :
505+ return _unit_map [value ]
506+
507+ # m is case significant
508+ if value .lower () in _unit_map and not value .startswith ('m' ):
509+ return _unit_map [value .lower ()]
510+
511+ return value
512+
513+ unit = {k : f (k ) for k in arg .keys ()}
514+ unit_rev = {v : k for k , v in unit .items ()}
515+
516+ # we require at least Ymd
517+ required = ['year' , 'month' , 'day' ]
518+ req = sorted (list (set (required ) - set (unit_rev .keys ())))
519+ if len (req ):
520+ raise ValueError ("to assemble mappings with a dict of "
521+ "units, requires year, month, day: "
522+ "[{0}] is missing" .format (',' .join (req )))
523+
524+ # keys we don't recognize
525+ excess = sorted (list (set (unit_rev .keys ()) - set (_unit_map .values ())))
526+ if len (excess ):
527+ raise ValueError ("extra keys have been passed "
528+ "to the datetime assemblage: "
529+ "[{0}]" .format (',' .join (excess )))
530+
531+ def coerce (values ):
532+ # we allow coercion to if errors allows
533+ return to_numeric (values , errors = errors )
534+
535+ values = (coerce (arg [unit_rev ['year' ]]) * 10000 +
536+ coerce (arg [unit_rev ['month' ]]) * 100 +
537+ coerce (arg [unit_rev ['day' ]]))
538+ try :
539+ values = to_datetime (values , format = '%Y%m%d' , errors = errors )
540+ except (TypeError , ValueError ) as e :
541+ raise ValueError ("cannot assemble the "
542+ "datetimes: {0}" .format (e ))
543+
544+ for u in ['h' , 'm' , 's' , 'ms' , 'us' , 'ns' ]:
545+ value = unit_rev .get (u )
546+ if value is not None and value in arg :
547+ try :
548+ values += to_timedelta (coerce (arg [value ]),
549+ unit = u ,
550+ errors = errors )
551+ except (TypeError , ValueError ) as e :
552+ raise ValueError ("cannot assemble the datetimes "
553+ "[{0}]: {1}" .format (value , e ))
554+
555+ return values
556+
420557
421558def _attempt_YYYYMMDD (arg , errors ):
422559 """ try to parse the YYYYMMDD/%Y%m%d format, try to deal with NaT-like,
0 commit comments