Могут ли панды автоматически распознавать даты?
Сегодня я был приятно удивлен тем фактом, что при чтении данных из файла данных (например) панды могут распознавать типы значений:
df = pandas.read_csv('test.dat', delimiter=r"\s+", names=['col1','col2','col3'])
Например, это можно проверить следующим образом:
for i, r in df.iterrows():
print type(r['col1']), type(r['col2']), type(r['col3'])
В частности, целое число, числа с плавающей точкой и строки были распознаны правильно. Однако у меня есть столбец с датами в следующем формате: 2013-6-4
, Эти даты были распознаны как строки (не как объекты даты Python). Есть ли способ "выучить" панд к признанным датам?
13 ответов
Вы должны добавить parse_dates=True
, или же parse_dates=['column name']
при чтении этого обычно достаточно для магического анализа. Но всегда есть странные форматы, которые нужно определять вручную. В таком случае вы также можете добавить функцию синтаксического анализа даты, которая является наиболее гибким способом.
Предположим, у вас есть столбец datetime с вашей строкой, а затем:
dateparse = lambda x: pd.datetime.strptime(x, '%Y-%m-%d %H:%M:%S')
df = pd.read_csv(infile, parse_dates=['datetime'], date_parser=dateparse)
Таким образом, вы даже можете объединить несколько столбцов в один столбец datetime, при этом столбцы "date" и "time" объединяются в один столбец "datetime":
dateparse = lambda x: pd.datetime.strptime(x, '%Y-%m-%d %H:%M:%S')
df = pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)
Вы можете найти директивы (то есть буквы, которые будут использоваться для разных форматов) для strptime
а также strftime
на этой странице.
Возможно, интерфейс pandas изменился после ответа @Rutger, но в версии, которую я использую (0.15.2), date_parser
Функция получает список дат вместо одного значения. В этом случае его код должен быть обновлен так:
dateparse = lambda dates: [pd.datetime.strptime(d, '%Y-%m-%d %H:%M:%S') for d in dates]
df = pd.read_csv(infile, parse_dates=['datetime'], date_parser=dateparse)
Вы могли бы использовать pandas.to_datetime()
как рекомендуется в документации для pandas.read_csv()
:
Если столбец или индекс содержит неразборчивую дату, весь столбец или индекс будет возвращен без изменений в качестве типа данных объекта. Для нестандартного анализа даты и времени используйте
pd.to_datetime
послеpd.read_csv
,
Демо-версия:
>>> D = {'date': '2013-6-4'}
>>> df = pd.DataFrame(D, index=[0])
>>> df
date
0 2013-6-4
>>> df.dtypes
date object
dtype: object
>>> df['date'] = pd.to_datetime(df.date, format='%Y-%m-%d')
>>> df
date
0 2013-06-04
>>> df.dtypes
date datetime64[ns]
dtype: object
При объединении двух столбцов в один столбец datetime принятый ответ генерирует ошибку (pandas версия 0.20.3), поскольку столбцы отправляются в функцию date_parser отдельно.
Следующие работы:
def dateparse(d,t):
dt = d + " " + t
return pd.datetime.strptime(dt, '%d/%m/%Y %H:%M:%S')
df = pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)
Метод pandas read_csv отлично подходит для разбора дат. Полная документация на http://pandas.pydata.org/pandas-docs/stable/generated/pandas.io.parsers.read_csv.html
Вы можете даже иметь разные части даты в разных столбцах и передавать параметр:
parse_dates : boolean, list of ints or names, list of lists, or dict
If True -> try parsing the index. If [1, 2, 3] -> try parsing columns 1, 2, 3 each as a
separate date column. If [[1, 3]] -> combine columns 1 and 3 and parse as a single date
column. {‘foo’ : [1, 3]} -> parse columns 1, 3 as date and call result ‘foo’
Стандартное восприятие дат прекрасно работает, но, похоже, оно смещено в сторону североамериканских форматов дат. Если вы живете в другом месте, вы можете время от времени попадаться на результаты. Насколько я помню, 1/6/2000 означает 6 января в США, а не 1 июня, где я живу. Это достаточно умно, чтобы качать их, если используются даты, такие как 23/6/2000. Вероятно, безопаснее остаться с вариациями даты ГГГГММДД. Прошу прощения у разработчиков панд, здесь, но я не проверял это с местными датами в последнее время.
Вы можете использовать параметр date_parser, чтобы передать функцию для преобразования вашего формата.
date_parser : function
Function to use for converting a sequence of string columns to an array of datetime
instances. The default uses dateutil.parser.parser to do the conversion.
Да - согласно pandas.read_csv
документация:
Примечание. Для дат в формате iso8601 существует быстрый путь.
Так что если ваш CSV имеет столбец с именем datetime
и даты выглядят так 2013-01-01T01:01
например, запуск этого приведет к тому, что pandas (я на v0.19.2) автоматически выберет дату и время:
df = pd.read_csv('test.csv', parse_dates=['datetime'])
Обратите внимание, что вам нужно явно передать parse_dates
, это не работает без.
Проверьте с помощью:
df.dtypes
Вы должны увидеть тип данных столбца datetime64[ns]
В дополнение к тому, что было сказано в других ответах, если вам нужно проанализировать очень большие файлы с сотнями тысяч отметок времени, date_parser
может оказаться огромным узким местом для производительности, поскольку это функция Python, вызываемая один раз для каждой строки. Вы можете значительно улучшить производительность, если вместо этого сохраните даты в виде текста при синтаксическом анализе файла CSV и затем конвертируете весь столбец в даты за один раз:
# For a data column
df = pd.read_csv(infile, parse_dates={'mydatetime': ['date', 'time']})
df['mydatetime'] = pd.to_datetime(df['mydatetime'], exact=True, cache=True, format='%Y-%m-%d %H:%M:%S')
# For a DateTimeIndex
df = pd.read_csv(infile, parse_dates={'mydatetime': ['date', 'time']}, index_col='mydatetime')
df.index = pd.to_datetime(df.index, exact=True, cache=True, format='%Y-%m-%d %H:%M:%S')
# For a MultiIndex
df = pd.read_csv(infile, parse_dates={'mydatetime': ['date', 'time']}, index_col=['mydatetime', 'num'])
idx_mydatetime = df.index.get_level_values(0)
idx_num = df.index.get_level_values(1)
idx_mydatetime = pd.to_datetime(idx_mydatetime, exact=True, cache=True, format='%Y-%m-%d %H:%M:%S')
df.index = pd.MultiIndex.from_arrays([idx_mydatetime, idx_num])
В моем случае использования файла с 200 тыс. Строк (одна временная метка на строку) сокращается время обработки примерно с минуты до менее секунды.
При загрузке файла csv содержится столбец даты. У нас есть два подхода, чтобы заставить pandas распознавать столбец даты, т.е.
Панды явно распознают формат по arg
date_parser=mydateparser
Панды неявно распознают формат по agr
infer_datetime_format=True
Некоторые данные столбца даты
01.01.18
02.01.18
Здесь мы не знаем первых двух вещей. Это может быть месяц или день. Итак, в этом случае мы должны использовать метод 1:- Явная передача формата
mydateparser = lambda x: pd.datetime.strptime(x, "%m/%d/%y")
df = pd.read_csv(file_name, parse_dates=['date_col_name'],
date_parser=mydateparser)
Метод 2:- Неявное или автоматическое распознавание формата
df = pd.read_csv(file_name, parse_dates=[date_col_name],infer_datetime_format=True)
Чтение существующих строковых столбцов в формате даты и времени соответственно.
pd.read_csv('CGMData.csv', parse_dates=['Date', 'Time'])
Результирующие столбцы
Объединить строковые столбцы даты и времени и добавить новый столбец объекта типа даты - удалить исходные столбцы
- если вы хотите переименовать новое имя столбца, то передайте его как словарь, как показано в приведенном ниже примере, и новое имя столбца будет именем ключа,
- если передать как список столбцов, новое имя столбца будет объединено с именем столбца, переданным в списке, разделенным _, например, Date_Time
# parse_dates={'given_name': ['Date', 'Time']}
pd.read_csv("InsulinData.csv",low_memory=False,
parse_dates=[['Date', 'Time']])
pd.read_csv("InsulinData.csv",low_memory=False,
parse_dates={'date_time': ['Date', 'Time']})
Объединить столбцы строки даты и времени и добавить новый столбец объекта типа даты и сохранить исходные столбцы.
pd.read_csv("InsulinData.csv",low_memory=False,
parse_dates=[['Date', 'Time']], keep_date_col=True)
Хотите изменить формат даты и времени при чтении из csv
parser = lambda x: pd.to_datetime(x, format='%Y-%m-%d %H:%M:%S')
pd.read_csv('path', date_parser=parser, parse_dates=['date', 'time'])
Да, этот код работает как ветер. Здесь индекс 0 относится к индексу столбца даты.
df = pd.read_csv(filepath, parse_dates=[0], infer_datetime_format = True)
Вы можете использовать параметр
date_parser
с функцией преобразования последовательности строковых столбцов в массив экземпляров datetime:
parser = lambda x: pd.to_datetime(x, format='%Y-%m-%d %H:%M:%S')
pd.read_csv('path', date_parser=parser, parse_dates=['date_col1', 'date_col2'])
Если производительность имеет значение для вас, убедитесь, что вы время:
import sys
import timeit
import pandas as pd
print('Python %s on %s' % (sys.version, sys.platform))
print('Pandas version %s' % pd.__version__)
repeat = 3
numbers = 100
def time(statement, _setup=None):
print (min(
timeit.Timer(statement, setup=_setup or setup).repeat(
repeat, numbers)))
print("Format %m/%d/%y")
setup = """import pandas as pd
import io
data = io.StringIO('''\
ProductCode,Date
''' + '''\
x1,07/29/15
x2,07/29/15
x3,07/29/15
x4,07/30/15
x5,07/29/15
x6,07/29/15
x7,07/29/15
y7,08/05/15
x8,08/05/15
z3,08/05/15
''' * 100)"""
time('pd.read_csv(data); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"]); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
'infer_datetime_format=True); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
'date_parser=lambda x: pd.datetime.strptime(x, "%m/%d/%y")); data.seek(0)')
print("Format %Y-%m-%d %H:%M:%S")
setup = """import pandas as pd
import io
data = io.StringIO('''\
ProductCode,Date
''' + '''\
x1,2016-10-15 00:00:43
x2,2016-10-15 00:00:56
x3,2016-10-15 00:00:56
x4,2016-10-15 00:00:12
x5,2016-10-15 00:00:34
x6,2016-10-15 00:00:55
x7,2016-10-15 00:00:06
y7,2016-10-15 00:00:01
x8,2016-10-15 00:00:00
z3,2016-10-15 00:00:02
''' * 1000)"""
time('pd.read_csv(data); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"]); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
'infer_datetime_format=True); data.seek(0)')
time('pd.read_csv(data, parse_dates=["Date"],'
'date_parser=lambda x: pd.datetime.strptime(x, "%Y-%m-%d %H:%M:%S")); data.seek(0)')
печатает:
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 03:13:28)
[Clang 6.0 (clang-600.0.57)] on darwin
Pandas version 0.23.4
Format %m/%d/%y
0.19123052499999993
8.20691274
8.143124389
1.2384357139999977
Format %Y-%m-%d %H:%M:%S
0.5238807110000039
0.9202787830000005
0.9832778819999959
12.002349824999996
Так с датой в формате iso8601 (%Y-%m-%d %H:%M:%S
по-видимому, это дата в формате iso8601, я думаю, T можно отбросить и заменить пробелом) указывать не нужно infer_datetime_format
(что, по-видимому, не имеет значения и для более распространенных) и передача вашего собственного парсера просто снижает производительность. С другой стороны, date_parser
действительно имеет значение с не столь стандартными дневными форматами. Будьте уверены, что время, прежде чем оптимизировать, как обычно.
Нет, в пандах нет возможности автоматически распознавать столбцы даты.
Pandas плохо справляется с выводом типов. Он в основном помещает большинство столбцов в качестве общих
object
введите, если вы не обойдете это вручную, например. используя вышеупомянутый параметр.
Если вы хотите автоматически определять типы столбцов, вам придется использовать отдельный инструмент профилирования данных, например. видений , а затем преобразовать или скормить выведенные типы обратно в ваш
DataFrame
конструктор (например, для дат и
from_csv
, с использованием
parse_dates
параметр).