Pandas to_sql为ValueError提供了时区感知列

时间:2016-02-16 14:40:51

标签: python postgresql pandas sqlalchemy

我想使用pandas to_sql在postgresql中存储一个时区感知列。

当时间不是时区感知时,它可以正常工作

times = ['201510100222', '201510110333']
df = pd.DataFrame()
df['time'] = pd.to_datetime(times)
df.time.to_sql('test', engine, if_exists='replace', index=False)

但是当我指定UTC

times = ['201510100222', '201510110333']
df = pd.DataFrame()
df['time'] = pd.to_datetime(times, utc=True)
df.time.to_sql('test', engine, if_exists='replace', index=False)

我有以下错误:

ValueError: Cannot cast DatetimeIndex to dtype datetime64[us]

我使用的是python 3.4.3,postgresql 9.4,pandas 0.17.1,sqlalchemy 1.0.5

4 个答案:

答案 0 :(得分:6)

您必须将其存储为PostgreSQL中的pd.Timestamp。下面的代码对我有用:

times = ['201510100222', '201510110333']
df = pd.DataFrame()
df['time'] = pd.to_datetime(times, utc=True)
df['time'] = df['time'].astype(pd.Timestamp)
df.time.to_sql('test', engine, if_exists='replace', index=False)

但是不要忘记正确创建数据类型为TIMESTAMP WITH TIME ZONE的数据库表。如果要直接从to_sql命令构建表,则必须明确指定它:

from sqlalchemy.types import TIMESTAMP as typeTIMESTAMP
df.time.to_sql('test', engine, if_exists='replace', index=False,dtype=typeTIMESTAMP(timezone=True))

答案 1 :(得分:2)

您可以将日期时间转换为字符串:

from sqlalchemy import TIMESTAMP
df.to_sql('test', engine, if_exists='replace', index=False,
          dtype={'time': TIMESTAMP(timezone=True)})

然后将它们作为日期时间插入数据库:

test=# select * from test;
          time          
------------------------
 2015-10-10 04:22:00+02
 2015-10-11 05:33:00+02
(2 rows)

这是一个非常丑陋的解决方案,但在我的设置上,它可行。

请注意,postgres会在当前时区显示日期时间。我的是欧洲/巴黎,所以这就是我查询它时得到的结果(psql):

          time          
------------------------
 2015-10-10 02:22:00+00
 2015-10-11 03:33:00+00

而不是

module LinkToExtensionHelper
  def mailer_link_to(name = nil, options = nil, html_options = nil, &block)
    # edit or change name, options, or html_options here like you want
    link = link_to(name, options, html_options, &block)
    if block_given?
      yield(link)
    else
      link
    end
  end
end

答案 2 :(得分:0)

这适用于pandas 0.16.2,所以你可以简单地降级pandas以避免错误:

conda remove pandas
conda install pandas=0.16.2

在数据库中:

(1)在postgresq.conf中设置timezone ='UTC'。这使得UTC成为数据库的所有连接的默认时区

(2)对数据库中的所有时间戳列使用带时区(也称为timestamptz)的时间戳。它们将值存储为UTC,但在选择时将它们转换为您的时区设置。

IN PYTHON:

(3)始终使用UTC中的时区创建时间戳:

def get_now_in_utc():
    now = datetime.now(tz=pytz.utc)
    return now

(4)并将它们与pandas to_sql持续存在

<强>结果:

(5)这将使你的持久性成为时区意识和准确。

(6)当从DB查询(在查询中使用AT TIME ZONE表达式)或在代码中(python中的时区转换)时,您始终可以获取UTC时间并将其转换为您喜欢的任何内容。

答案 3 :(得分:0)

我发现这很适合我(甚至允许进行tz转换):

from sqlalchemy.types import TIMESTAMP as typeTIMESTAMP
df = pd.DataFrame({
    'datetime': ['2000-10-29 00:00', '2000-10-29 01:00', '2000-10-29 02:00', '2000-10-29 03:00'],
    'a': range(4),
})
df.datetime = [pd.Timestamp(i, tz='UTC').tz_convert(tz='Europe/London') for i in df.datetime]
df.datetime = df.datetime.astype(pd.Timestamp)  # needs to re-convert to Timestamp
df.to_sql(name=TEST_TABLE_NAME, con=conn, index=False, dtype={'datetime': typeTIMESTAMP(timezone=True)})

# if you want to set it as primary key
conn.execute("""ALTER TABLE "{}" ADD PRIMARY KEY (datetime);""".format(TABLE_NAME))