我应该在我的包中使用 S3 数据帧吗?
Should I use S3 data frames in my package?
我有一个使用基于 data.frame 的 S4 的软件包 class:
setClass(Class="foobar",
slots=c(a="character", b="character", c="character"),
contains="data.frame")
按预期工作。但是,在与 tidyverse 结合使用时,我观察到奇怪的警告:
df <- data.frame(ID=1:5)
df2 <- new("foobar", df)
as_tibble(df2)
最后一条语句引发警告消息:
Warning message:
In class(x) <- c(subclass, tibble_class) :
Setting class(x) to multiple strings ("tbl_df", "tbl", ...); result will no longer be an S4 object
这是因为 tidyverse does not support S4 data frames。这可以通过使用 asS3(df)
在下游代码中规避。但是,如果我的包的用户看到这些警告,他们可能会感到困惑。我现在面临以下选择,我真的不知道哪个是最合理和正确的:
- 保留 S4 模型,希望用户每次将我的数据帧传递到其他东西时都不会介意看到此警告。
- 使用S3。但是,我已经在我的包的已发布版本中定义了另一个 S4 class。我怕我会破坏别人的代码。
- 混合 S3 和 S4。甚至允许吗?
还有其他我可能忽略的解决方案吗?
没有完全在您控制范围内的完美解决方案。
tidyverse
包可能会调用 class<-
给它的任何类似数据框的对象,如您所见,这将破坏任何对象的 S4 性质。这不能通过(例如)为 coerce
定义方法或调用 setAs
来解决,因为 class<-
不使用该机制。 (class<-
也不是通用的,你不能为它设置方法。)使 tidyverse
支持 S4 的唯一方法是 tidyverse
的作者将代码更改为使用 as
或类似的,它看起来不像是他们的待办事项列表的顶部。
当您发布了一个已经包含 S4 class 的软件包版本时,您担心会显着改变 class 的工作方式是正确的。
如果:
- 您的软件包很新,用户还不多;
- 你可以用 S3 做你需要做的一切;和
- 你不知道还有另一个包在你的包之上构建了新的 classes
那么最好将其重新定义为 S3,并在安装或加载包时包含一条消息说
thanks for installing myPackage v2. Code may be incompatible with v1.2 or earlier; see help(blah) for details
否则,坚持使用S4。
您不能将 S3 和 S4 完全混合用于 class 定义(可以用于方法定义)。最接近的是 setOldClass
,它将 S3 class 注册为 S4(而您想要相反)。不过,这可能会帮助您实现上述 "you can do all you need to do with S3"。
另一种可能性是定义您自己的 class<-
版本,它检查 S4 class foobar
的对象是否试图被强制转换为 S3
如果不是,则调用普通的 class<-
。在这种情况下,治愈可能比疾病更糟;这将减慢 all 未来的 S3 class 转换(因为 class<-
现在是一个普通的函数调用,而不是原始函数)但它应该在原则上工作。不推荐的另一个原因是您不依赖搜索路径中更高层的其他包来做类似的事情(如果另一个包作者有同样的问题并想做同样的事情怎么办?那么结果将取决于哪个包在 search
路径上更高!)
我有一个使用基于 data.frame 的 S4 的软件包 class:
setClass(Class="foobar",
slots=c(a="character", b="character", c="character"),
contains="data.frame")
按预期工作。但是,在与 tidyverse 结合使用时,我观察到奇怪的警告:
df <- data.frame(ID=1:5)
df2 <- new("foobar", df)
as_tibble(df2)
最后一条语句引发警告消息:
Warning message:
In class(x) <- c(subclass, tibble_class) :
Setting class(x) to multiple strings ("tbl_df", "tbl", ...); result will no longer be an S4 object
这是因为 tidyverse does not support S4 data frames。这可以通过使用 asS3(df)
在下游代码中规避。但是,如果我的包的用户看到这些警告,他们可能会感到困惑。我现在面临以下选择,我真的不知道哪个是最合理和正确的:
- 保留 S4 模型,希望用户每次将我的数据帧传递到其他东西时都不会介意看到此警告。
- 使用S3。但是,我已经在我的包的已发布版本中定义了另一个 S4 class。我怕我会破坏别人的代码。
- 混合 S3 和 S4。甚至允许吗?
还有其他我可能忽略的解决方案吗?
没有完全在您控制范围内的完美解决方案。
tidyverse
包可能会调用 class<-
给它的任何类似数据框的对象,如您所见,这将破坏任何对象的 S4 性质。这不能通过(例如)为 coerce
定义方法或调用 setAs
来解决,因为 class<-
不使用该机制。 (class<-
也不是通用的,你不能为它设置方法。)使 tidyverse
支持 S4 的唯一方法是 tidyverse
的作者将代码更改为使用 as
或类似的,它看起来不像是他们的待办事项列表的顶部。
当您发布了一个已经包含 S4 class 的软件包版本时,您担心会显着改变 class 的工作方式是正确的。
如果:
- 您的软件包很新,用户还不多;
- 你可以用 S3 做你需要做的一切;和
- 你不知道还有另一个包在你的包之上构建了新的 classes
那么最好将其重新定义为 S3,并在安装或加载包时包含一条消息说
thanks for installing myPackage v2. Code may be incompatible with v1.2 or earlier; see help(blah) for details
否则,坚持使用S4。
您不能将 S3 和 S4 完全混合用于 class 定义(可以用于方法定义)。最接近的是 setOldClass
,它将 S3 class 注册为 S4(而您想要相反)。不过,这可能会帮助您实现上述 "you can do all you need to do with S3"。
另一种可能性是定义您自己的 class<-
版本,它检查 S4 class foobar
的对象是否试图被强制转换为 S3
如果不是,则调用普通的 class<-
。在这种情况下,治愈可能比疾病更糟;这将减慢 all 未来的 S3 class 转换(因为 class<-
现在是一个普通的函数调用,而不是原始函数)但它应该在原则上工作。不推荐的另一个原因是您不依赖搜索路径中更高层的其他包来做类似的事情(如果另一个包作者有同样的问题并想做同样的事情怎么办?那么结果将取决于哪个包在 search
路径上更高!)