【问题标题】:rpy2 ggplot2 Error: Invalid input: date_trans works with objects of class Date onlyrpy2 ggplot2 错误:输入无效:date_trans 仅适用于 Date 类的对象
【发布时间】:2018-12-04 11:18:14
【问题描述】:

我正在尝试使用 rpy2 包从 python 脚本调用 ggplot2 来绘制时间序列数据。当我尝试调整 x 刻度的日期限制时出现错误。 rpy2 文档提供了此指南 (https://rpy2.readthedocs.io/en/version_2.8.x/vector.html?highlight=date%20vector):“日期或时间点的序列可以存储在 POSIXltPOSIXct 对象中。两者都可以从 time.struct_time 对象的 Python 序列或 R 对象中创建。”

这是我的示例代码:

import numpy as np
import pandas as pd
import datetime as dt
from rpy2 import robjects as ro
from rpy2.robjects import pandas2ri
import rpy2.robjects.lib.ggplot2 as ggplot2
pandas2ri.activate()

#Create a random dataframe with time series data
df = pd.DataFrame({'Data': np.random.normal(50, 5, 10),
                  'Time': [dt.datetime(2000, 7, 23), dt.datetime(2001, 7, 15),
                           dt.datetime(2002, 7, 30), dt.datetime(2003, 8, 5),
                           dt.datetime(2004, 6, 28), dt.datetime(2005, 7, 23),
                           dt.datetime(2006, 7, 15), dt.datetime(2007, 7, 30),
                           dt.datetime(2008, 8, 5), dt.datetime(2009, 6, 28)]})

#Create a POSIXct vector from time.struct_time objects to store the x limits
date_min = dt.datetime(2000, 1, 1).timetuple()
date_max = dt.datetime(2010, 1, 1).timetuple()
date_range = ro.vectors.POSIXct((date_min, date_max))

#Generate the plot
gp = ggplot2.ggplot(df)
gp = (gp + ggplot2.aes_string(x='Time', y='Data') +
      ggplot2.geom_point() +
      ggplot2.scale_x_date(limits=date_range))

当我运行此代码时,我收到以下错误消息:

Error: Invalid input: date_trans works with objects of class Date only

除了POSIXct 对象,我还尝试了DateVector 对象。我还尝试使用base.as_Date() 将日期字符串转换为 R 日期并将它们输入到 R 矢量对象中。我总是收到相同的错误消息。在 R 中,我会像这样更改比例限制:

gp + scale_x_date(limits = as.Date(c("2000/01/01", "2010/01/01"))

如何将它翻译成 rpy2 以便我的 python 脚本能够运行?

【问题讨论】:

  • 这是一个非常有趣的问题,帮助我了解了 R 的一些内部知识!您正在使用 Python 的 + 运算符,它不是 ggplot2 的 +!将立即发布答案。

标签: python r ggplot2 rpy2


【解决方案1】:

考虑像在 R 中一样运行基本 R 函数,您可以将其作为库导入 rpy2。仅供参考 - 在 R 会话中 basestatsutils 和其他内置库在没有 library 行的情况下被隐式加载。

日期时间处理

此外,使用strftime 而不是timetuple() 将Python 日期时间对象转换为字符串,以便于翻译。

base = importr('base')
...
date_min = dt.datetime(2000, 1, 1).strftime('%Y-%m-%d')
date_max = dt.datetime(2010, 1, 1).strftime('%Y-%m-%d')
date_range = base.as_POSIXct(base.c(date_min, date_max), format="%Y-%m-%d")
...
ggplot2.scale_x_datetime(limits=date_range))

GGPlot Plus 运算符

此外,+ Python 运算符与 ggplot2 的不太一样,实际上是:ggplot2:::`+.gg`。正如这篇 SO 帖子 How is ggplot2 plus operator defined? 中所指出的,此函数有条件地运行 add_theme()add_ggplot(),您需要在 Python 中复制它们。因为上面的 R 函数是一个本地命名空间,在 ggplot2.* 调用时不容易获得,所以使用 R 的 utils::getAnywhere("+.gg") 将函数作为用户定义的方法导入。

因此,您需要将+ 转换为Python 对象模型的实际合格调用。您可以使用 base R 的Reduce 来实现这一点。所以R中的以下内容:

gp <- ggplot(df)
gp <- gp + aes_string(x='Time', y='Data') +
  geom_point() +
  scale_x_datetime(limits=date_range)

相当于

gp <- Reduce(ggplot2:::`+.gg`, list(ggplot(df), aes_string(x='Time', y='Data'), 
                                    geom_point(), scale_x_datetime(limits=date_range)))

或者在会话中加载 ggplot2 库后使用getAnywhere()

gg_proc <- getAnywhere("+.gg")

gp <- Reduce(gg_proc$objs[[1]], list(ggplot(df), aes_string(x='Time', y='Data'), 
                                     geom_point(), scale_x_datetime(limits=date_range)))

Rpy2

以下是 rpy2 中的完整代码。因为您以非交互方式运行在 Python 脚本中分层的 R 对象,所以绘图不会显示在屏幕上,需要保存,这可以通过 ggsave 实现:

import numpy as np
import pandas as pd
import datetime as dt

from rpy2.robjects import pandas2ri
from rpy2.robjects.packages import importr

# IMPORT R PACKAGES
base = importr('base')
utils = importr('utils')
ggplot2 = importr('ggplot2')

pandas2ri.activate()

# CREATE RANDOM (SEEDED) DATAFRAME WITH TIME SERIES DATA
np.random.seed(6252018)
df = pd.DataFrame({'Data': np.random.normal(50, 5, 10),
                   'Time': [dt.datetime(2000, 7, 23), dt.datetime(2001, 7, 15),
                            dt.datetime(2002, 7, 30), dt.datetime(2003, 8, 5),
                            dt.datetime(2004, 6, 28), dt.datetime(2005, 7, 23),
                            dt.datetime(2006, 7, 15), dt.datetime(2007, 7, 30),
                            dt.datetime(2008, 8, 5), dt.datetime(2009, 6, 28)]})

# CONVERT TO POSIXct VECTOR
date_min = dt.datetime(2000, 1, 1).strftime('%Y-%m-%d')
date_max = dt.datetime(2010, 1, 1).strftime('%Y-%m-%d')
date_range = base.as_POSIXct(base.c(date_min, date_max), format="%Y-%m-%d")

# RETRIEVE NEEDED FUNCTION
gg_plot_func = utils.getAnywhere("+.gg")

# PRODUCE PLOT
gp = base.Reduce(gg_plot_func[1][0], base.list(ggplot2.ggplot(df),
                                               ggplot2.aes_string(x='Time', y='Data'),
                                               ggplot2.geom_point(),
                                               ggplot2.scale_x_datetime(limits=date_range)))
# SAVE PLOT TO DISK
ggplot2.ggsave(filename="myPlot.png", plot=gp, device="png", path="/path/to/plot/output")

输出 (用 Python 渲染)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 2020-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多