Pandalar tarihleri ​​otomatik olarak tanıyabilir mi?


161

Bugün, pandaların bir veri dosyasından (örneğin) veri okurken değer türlerini tanıyabildikleri gerçeğine çok şaşırdım:

df = pandas.read_csv('test.dat', delimiter=r"\s+", names=['col1','col2','col3'])

Örneğin şu şekilde kontrol edilebilir:

for i, r in df.iterrows():
    print type(r['col1']), type(r['col2']), type(r['col3'])

Özellikle tamsayı, yüzer sayılar ve dizeler doğru bir şekilde tanındı. Ancak, aşağıdaki biçimde tarihlerine sahip bir sütunu vardır: 2013-6-4. Bu tarihler dizeler olarak kabul edildi (python tarih nesneleri olarak değil). Pandaları tanınan tarihlere "öğrenmenin" bir yolu var mı?


Lütfen bu tür sürüme bağlı sorular için her zaman pandaların sürümünü belirtin. In Temmuz 2013'te, bu v0.11 olurdu
SMCI

Ve dtipler her sütun için sabittir, her df.iterrows()satır için tekrarlamanıza ve bunları görüntülemenize gerek yoktur, sadece bir df.info()kez yapın.
smci

Yanıtlar:


343

Sen eklemek gerekir parse_dates=True, ya da parse_dates=['column name']okurken, şu genellikle yeterli sihirli ayrıştırmak. Ancak her zaman manuel olarak tanımlanması gereken garip formatlar vardır. Böyle bir durumda, mümkün olan en esnek yol olan bir tarih ayrıştırıcı işlevi de ekleyebilirsiniz.

Dizinizle birlikte bir 'tarih saat' sütununuz olduğunu varsayalım, ardından:

from datetime import datetime
dateparse = lambda x: datetime.strptime(x, '%Y-%m-%d %H:%M:%S')

df = pd.read_csv(infile, parse_dates=['datetime'], date_parser=dateparse)

Bu şekilde, birden çok sütunu tek bir tarih saat sütununda birleştirebilirsiniz; bu, bir 'tarih' ve bir 'saat' sütununu tek bir 'tarih saat' sütununda birleştirir:

dateparse = lambda x: datetime.strptime(x, '%Y-%m-%d %H:%M:%S')

df = pd.read_csv(infile, parse_dates={'datetime': ['date', 'time']}, date_parser=dateparse)

Bu sayfadastrptime ve için yönergeleri (yani farklı formatlar için kullanılacak harfleri) strftime bulabilirsiniz .


8
Benim için işe yaramadı, şu hatayı aldım:TypeError: strptime() argument 1 must be str, not float
Jean Paul

6
Bu hatayı aldım çünkü veri çerçevemde nan vardı.
Jean Paul

1
Bir seçenek vardır infer_datetime_format: "Pandalar, sütunlardaki tarih-saat dizelerinin biçimini çıkarmaya çalışır". Bunun yerine kullanılabilir date_parser.
Winand

1
Tarihleriniz ISO 8601formattaysa, geçmemeniz infer_datetime_formatveya bir ayrıştırıcı işlev görmemeniz gerektiğini unutmayın - pandaların bunu işlemesine izin vermekten çok daha yavaştır (özellikle ikincisi). Bu yanıttaki tarih biçimi de bu kategoriye girer
Mr_and_Mrs_D

1
pd.datetimeşu anda değiştirin kaldırılmış pd.datetimesadece ile datetimesonra import datetime from datetime.
Ébe Isaac

20

@Rutger yanıtladığından beri pandaların arayüzü değişmiş olabilir, ancak benim kullandığım sürümde (0.15.2) date_parserişlev, tek bir değer yerine bir tarih listesi alıyor. Bu durumda, kodu şu şekilde güncellenmelidir:

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)

11

Pandas read_csv yöntemi tarihleri ​​ayrıştırmak için harikadır. Http://pandas.pydata.org/pandas-docs/stable/generated/pandas.io.parsers.read_csv.html adresinde eksiksiz belgeler

Hatta farklı sütunlarda farklı tarih bölümlerine sahip olabilir ve parametreyi iletebilirsiniz:

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’

Varsayılan tarih algılama harika çalışıyor, ancak Kuzey Amerika Tarih biçimlerine karşı önyargılı görünüyor. Başka bir yerde yaşıyorsanız, zaman zaman sonuçlara yakalanabilirsiniz. Hatırlayabildiğim kadarıyla 1/6/2000, yaşadığım yerdeki 1 Haziran yerine ABD'de 6 Ocak anlamına geliyor. 23/6/2000 gibi tarihler kullanılıyorsa, onları sallamak yeterince akıllıdır. Yine de YYYYAAGG tarih varyasyonları ile kalmak muhtemelen daha güvenli. Pandas geliştiricilerinden özür dilerim, ama yakın zamanda yerel tarihlerle test etmedim.

formatınızı dönüştürmek üzere bir işlevi iletmek için date_parser parametresini kullanabilirsiniz.

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.

2
dayfirstAvrupa / uluslararası tarihler için True olarak belirtebilirsiniz . pandas.pydata.org/pandas-docs/stable/generated/…
Will Gordon

10

Aşağıdakiler pandas.to_datetime()için belgelerde önerildiği gibi kullanabilirsiniz pandas.read_csv():

Bir sütun veya dizin, karşılaştırılamaz bir tarih içeriyorsa, tüm sütun veya dizin, nesne veri türü olarak değiştirilmeden döndürülür. Standart olmayan tarih saat ayrıştırması için pd.to_datetimeafter pd.read_csv.

Demo:

>>> 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

nesne türündeki diğer sütunları da bugüne dönüştürüyor
ratnesh

10

İki sütunu tek bir tarih-saat sütununda birleştirirken, sütunlar date_parser işlevine ayrı olarak gönderildiği için kabul edilen yanıt bir hata oluşturur (pandalar sürüm 0.20.3).

Aşağıdaki işler:

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)

1
Pandalar 0.22 kullanıyorum ve kabul edilen cevabın artık işe yaramadığını kabul ediyorum.
Dai

Bu bir "TypeError yaratır: benim için yalnızca str'yi (" float "değil) str'ye" birleştirebilir. Tarih sütunu d / m / y ve saat sütunu H: M: 00
IceQueeny

8

Evet - pandas.read_csv belgelere göre :

Not: iso8601 formatlı tarihler için hızlı bir yol mevcuttur .

Dolayısıyla , csv'nizde adlandırılmış bir sütun varsa datetimeve 2013-01-01T01:01örneğin tarihler gibi görünüyorsa , bunu çalıştırmak pandaların (v0.19.2'deyim) tarih ve saati otomatik olarak almasını sağlar:

df = pd.read_csv('test.csv', parse_dates=['datetime'])

Açıkça geçmeniz gerektiğini unutmayın, parse_datesonsuz çalışmaz.

Şununla doğrula:

df.dtypes

Sütunun veri türünü görmelisiniz datetime64[ns]


Sanırım soruyu yanlış anladınız. Kullanıcı, bu seçeneğin kendi dize biçimi için etkinleştirilip etkinleştirilemeyeceğini merak ediyor.
Arya McCarthy

@AryaMcCarthy umm, temelde tarihin doğru tanınmasını istiyor, bu yüzden pandalar tarafından doğal olarak tanınması için kaynak veriyi nasıl dönüştürebileceğinden bahsediyorum. Hiçbir yerde kaynak verilerin formatını değiştiremeyeceğinden bahsetmiyor.
Gaurav

2

Csv dosyası yüklenirken tarih sütunu içerir.Pandaların tarih sütununu tanımasını sağlamak için iki yaklaşımımız var.

  1. Pandalar, biçimi açıkça arg tarafından tanır date_parser=mydateparser

  2. Pandalar, formatı agr tarafından örtük olarak tanır infer_datetime_format=True

Tarih sütunu verilerinden bazıları

01/01/18

01/02/18

Burada ilk iki şeyi bilmiyoruz Ay veya gün olabilir. Bu durumda, Yöntem 1'i kullanmalıyız: - Açık formatı geç

    mydateparser = lambda x: pd.datetime.strptime(x, "%m/%d/%y")
    df = pd.read_csv(file_name, parse_dates=['date_col_name'],
date_parser=mydateparser)

Yöntem 2: - Biçimi örtük veya otomatik olarak tanıma

df = pd.read_csv(file_name, parse_dates=[date_col_name],infer_datetime_format=True)

1

Performans sizin için önemliyse, zaman ayırdığınızdan emin olun:

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)')

baskılar:

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

Yani ISO8601 biçimli tarihle ( %Y-%m-%d %H:%M:%Sgörünüşte bir ISO8601 biçimli tarih, ben T tahmin olduğunu bırakılabilir sen gerektiğini ve bir boşluk ile değiştirilir) değil belirtmek infer_datetime_format(ya görünüşte daha yaygın olanları ile fark yapmaz) ve kendi geçirerek ayrıştırıcıda sadece performansı bozar. Öte yandan, date_parserçok standart olmayan gün formatlarıyla da fark yaratıyor. Her zamanki gibi optimize etmeden önce zaman ayırdığınızdan emin olun.


0

Diğer yanıtların söylediğine ek olarak, çok büyük dosyaları yüz binlerce zaman damgası ile ayrıştırmanız date_parsergerekiyorsa, her satırda bir kez çağrılan bir Python işlevi olduğu için büyük bir performans darboğazı oluşturabilir. Bunun yerine, CSV dosyasını ayrıştırırken tarihleri ​​metin olarak tutarak ve ardından tüm sütunu tek seferde tarihlere dönüştürerek önemli bir performans iyileştirmeleri elde edebilirsiniz:

# 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.000 satır (satır başına bir zaman damgası) içeren bir dosyadaki kullanım durumum için, işlem süresini yaklaşık bir dakikadan bir saniyenin altına düşürdü.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.