如何总结复杂对象中的元素?

时间:2017-01-11 21:01:10

标签: r

我有一个非常复杂的S4对象(来自lavaan模型的输出),其中插槽内的插槽和变量($)内的插槽位于每个最深插槽的最深层。如何提取和存储此对象中每个元素的object.size(以及可能的其他函数,如lengthdim以及对象名称),以便我可以将其与另一个对象进行比较同一个班级?

我已经尝试存储来自str(obj)unclass(obj)的输出,然后操纵输出以提取我想要的信息,但结果却非常繁琐。循环使用名字同样困难。有没有办法将对象“扁平”成列表?是否有任何人可以想到的递归函数反复挖掘每个插槽?

修改

以下是一个示例,使用上面引用的lavaan包,但理想情况下,解决方案不应该依赖于特定的对象类,并且可以跨类工作:

library(lavaan)
model <- '
  # measurement model
    ind60 =~ x1 + x2 + x3
    dem60 =~ y1 + y2 + y3 + y4
    dem65 =~ y5 + y6 + y7 + y8
  # regressions
    dem60 ~ ind60
    dem65 ~ ind60 + dem60
  # residual correlations
    y1 ~~ y5
    y2 ~~ y4 + y6
    y3 ~~ y7
    y4 ~~ y8
    y6 ~~ y8
'         
fit <- sem(model, data=PoliticalDemocracy)

对象fit里面包含许多插槽和对象。当然,我可以从object.size(fit@Data@X[[1]])等特定元素中提取信息,但我正在寻找一种通用的解决方案。挑战在于我想要提取有关每个元素的相同信息,无论其“深度”如何。

谢谢!

1 个答案:

答案 0 :(得分:-1)

似乎purrr包可能对此有帮助,尤其是flattentransposemap / at_depth等功能 - 与char矢量结合作为输入您可以轻松地从深层嵌套列表中提取内容。例如,你可以写下&#34;提取器&#34;您需要分离的函数,然后将它们全部存储在列表中,并使用invoke(也来自purrr)将您的对象作为唯一的arg或许多此类对象的invoke_map

修改
以下是一些代码,可帮助您从一个或多个object.size(fit@Data@X[[1]])对象中提取lavaan。由于您感兴趣的插槽,实际上很可能在不同的深度,我的猜测是没有简单的通用解决方案。
这个想法是,一旦你知道你感兴趣的确切元素 - 编写一些辅助函数来操作单个/多个这样的对象是相当简单的。我上面提到的功能为实现这一目标提供了友好的捷径 如果我可以提供进一步的帮助,请告诉我。

  library("lavaan")
  library("tidyverse")

  model <- '
  # measurement model
  ind60 =~ x1 + x2 + x3
  dem60 =~ y1 + y2 + y3 + y4
  dem65 =~ y5 + y6 + y7 + y8
  # regressions
  dem60 ~ ind60
  dem65 ~ ind60 + dem60
  # residual correlations
  y1 ~~ y5
  y2 ~~ y4 + y6
  y3 ~~ y7
  y4 ~~ y8
  y6 ~~ y8
  '         

  # say you have 3 different models
  fit1 <- sem(model, data=PoliticalDemocracy)
  fit2 <- sem(model, data=PoliticalDemocracy)
  fit3 <- sem(model, data=PoliticalDemocracy)

  # S4 objects have function slot() function for accessing so 
  object.size(fit@Data@X[[1]]) 
  # becomes
  slot(slot(fit, "Data"), "X")[[1]]

  # since fit is an S4 object - you have to wrap it up 
  # in a list to manipulate it easier.
  # above code becomes : 
  list(fit1) %>% 
    map(~ slot(., "Data")) %>% 
    map(~ slot(., "X")) %>%  
    flatten %>% 
    map(object.size)

  # wrap up the above code in a helper function ...
  extr_obj_size <- function(lavaan_fit) {
    list(lavaan_fit) %>% 
      map(~ slot(., "Data")) %>% 
      map(~ slot(., "X")) %>%  
      map(object.size) 
  }

  extr_obj_size(fit1)

  # which you can further wrap up in a function that operates 
  # on a vector of such objects
  extr_multiple_obj_size <- function(vec_lavaan_fits) {
    vec_lavaan_fits %>% 
      map(extr_obj_size) %>% 
      flatten

  }

  c(fit,fit2,fit3) %>% extr_multiple_obj_size

Edit2

我不知道以下代码对一般有多大帮助,但我混淆了一些东西,假设你知道你感兴趣的插槽名称 - 会检查深度1和2并返回相应的值。

fit <- sem(model, data=PoliticalDemocracy)
slot_of_interest <- "eq.constraints"

# slot names at depth 1
depth1 <- names(getSlots("lavaan"))
# slot names at depth 2
depth2 <- depth1 %>% map(~ slotNames(slot(fit,.)))

# helper fun to check if a slot name of interest is inside a slot
in_slot <- function(x) slot_of_interest %in% x

# so if its at depth 1 - then just map slot()-function with that name
if (slot_of_interest %in% depth1) {
  list(fit1) %>% map(~slot(., slot_of_interest))
} else {
  # otherwise you would need to detect at which index at depth2 does this name appear
  index1 <- depth2 %>% detect_index(in_slot)
  # and map first the slot-name at that index - then the corresponding slot of interest
  list(fit1) %>% map(~ slot(., depth1[index1])) %>% map(~ slot(., slot_of_interest))
}