【问题标题】:How can I find a dataset that has some specific attributes? [duplicate]如何找到具有某些特定属性的数据集? [复制]
【发布时间】:2018-02-23 17:17:01
【问题描述】:

datasets 包和各种包都带有大量有用的数据集,但是当您需要它用于包示例、教学目的或询问/在这里回答一个关于 SO 的问题。

例如,我想要一个数据集,它是 data.frame,至少有 2 个 character 列,并且长度小于 100 行。

如何探索每个可用的数据集并查看最多的相关信息来做出选择?

我过去的尝试很混乱,需要时间,并且由于某些具有不寻常对象结构的包(例如 caret)而崩溃。

【问题讨论】:

    标签: r


    【解决方案1】:

    我已经将一个解决方案打包在一个单一功能的 github 包中。

    我正在复制底部的整个代码,但最简单的是:

    remotes::install_github("moodymudskipper/datasearch")
    library(datasearch)
    

    “dplyr”包中的所有数据集

    dplyr_all <-
      datasearch("dplyr")
    
    View(dplyr_all)
    

    受条件限制的“数据集”包中的数据集

    datasets_ncol5 <-
      datasearch("datasets", filter =  ~is.data.frame(.) && ncol(.) == 5)
    
    View(datasets_ncol5)
    

    所有已安装包的所有数据集,无限制

    
    # might take more or less time, depends what you have installed
    all_datasets <- datasearch()
    
    View(all_datasets)
    
    # subsetting the output
    my_subset <- subset(
      all_datasets, 
      class1 == "data.frame" &
        grepl("treatment", names_collapsed) &
        nrow < 100
    )
    
    View(my_subset)
    


    datasearch <- function(pkgs = NULL, filter = NULL){
      # make function silent
      w <- options()$warn
      options(warn = -1)
      search_ <- search()
      file_ <- tempfile()
      file_ <- file(file_, "w")
      on.exit({
        options(warn = w)
        to_detach <- setdiff(search(), search_)
        for(pkg in to_detach) eval(bquote(detach(.(pkg))))
        # note : we still have loaded namespaces, we could unload those that we ddn't
        # have in the beginning but i'm worried about surprising effects, I think
        # the S3 method tables should be cleaned too, and maybe other things
    
        # note2 : tracing library and require didn't work
        })
    
      # convert formula to function
      if(inherits(filter, "formula")) {
        filter <- as.function(c(alist(.=), filter[[length(filter)]]))
      }
    
      ## by default fetch all available packages in .libPaths()
      if(is.null(pkgs)) pkgs <- .packages(all.available = TRUE)
      ## fetch all data sets description
      df <- as.data.frame(data(package = pkgs, verbose = FALSE)$results)
      names(df) <- tolower(names(df))
      item <- NULL # for cmd check note
      df <- transform(
        df,
        data_name = sub('.*\\((.*)\\)', '\\1', item),
        dataset   = sub(' \\(.*', '', item),
        libpath = NULL,
        item = NULL
        )
      df <- df[order(df$package, df$data_name),]
      pkg_data_names <- aggregate(dataset ~ package + data_name, df, c)
      pkg_data_names <- pkg_data_names[order(pkg_data_names$package, pkg_data_names$data_name),]
    
      env <- new.env()
      n <-  nrow(pkg_data_names)
      pb <- progress::progress_bar$new(
        format = "[:bar] :percent :pkg",
        total = n)
      row_dfs <- vector("list", n)
      for(i in seq(nrow(pkg_data_names))) {
        pkg    <- pkg_data_names$package[i]
        data_name <- pkg_data_names$data_name[i]
        datasets  <- pkg_data_names$dataset[[i]]
        pb$tick(tokens = list(pkg = format(pkg, width = 12)))
    
        sink(file_, type = "message")
        data(list=data_name, package = pkg, envir = env)
        row_dfs_i <- lapply(datasets, function(dataset) {
          dat <- get(dataset, envir = env)
          if(!is.null(filter) && !filter(dat)) return(NULL)
          cl <- class(dat)
          nms <- names(dat)
          nc <- ncol(dat)
          if (is.null(nc)) nc <- NA
          nr <- nrow(dat)
          if (is.null(nr)) nr <- NA
    
          out <- data.frame(
            package = pkg,
            data_name = data_name,
            dataset = dataset,
            class = I(list(cl)),
            class1 = cl[1],
            type = typeof(dat),
            names = I(list(nms)),
            names_collapsed = paste(nms, collapse = "/"),
            nrow       = nr,
            ncol       = nc,
            length     = length(dat))
    
          if("data.frame" %in% cl) {
            classes <- lapply(dat, class)
            cl_flat <- unlist(classes)
            out <- transform(
              out,
              classes    = I(list(classes)),
              types      = I(list(vapply(dat, typeof, character(1)))),
              logical    = sum(cl_flat == 'logical'),
              integer    = sum(cl_flat == 'integer'),
              numeric    = sum(cl_flat == 'numeric'),
              complex    = sum(cl_flat == 'complex'),
              character  = sum(cl_flat == 'character'),
              raw        = sum(cl_flat == 'raw'),
              list       = sum(cl_flat == 'list'),
              data.frame = sum(cl_flat == 'data.frame'),
              factor     = sum(cl_flat == 'factor'),
              ordered    = sum(cl_flat == 'ordered'),
              Date       = sum(cl_flat == 'Date'),
              POSIXt     = sum(cl_flat == 'POSIXt'),
              POSIXct    = sum(cl_flat == 'POSIXct'),
              POSIXlt    = sum(cl_flat == 'POSIXlt'))
          } else {
            out <- transform(
              out,
              nrow       = NA,
              ncol       = NA,
              classes    = NA,
              types      = NA,
              logical    = NA,
              integer    = NA,
              numeric    = NA,
              complex    = NA,
              character  = NA,
              raw        = NA,
              list       = NA,
              data.frame = NA,
              factor     = NA,
              ordered    = NA,
              Date       = NA,
              POSIXt     = NA,
              POSIXct    = NA,
              POSIXlt    = NA)
          }
          if(is.matrix(dat)) {
            out$names <- list(colnames(dat))
            out$names_collapsed = paste(out$names, collapse = "/")
          }
          out
        })
        row_dfs_i <- do.call(rbind, row_dfs_i)
        if(!is.null(row_dfs_i)) row_dfs[[i]] <- row_dfs_i
        sink(type = "message")
      }
      df2 <- do.call(rbind, row_dfs)
      df <- merge(df, df2)
      df
    }
    

    【讨论】:

      【解决方案2】:

      根据自己的喜好扩展/修改。

      library(data.table)
      dt = as.data.table(data(package = .packages(all.available = TRUE))$results)
      dt = dt[, `:=`(Item   = sub(' \\(.*', '', Item),
                     Object = sub('.*\\((.*)\\)', '\\1', Item))]
      
      dt[, { 
             data(list = Object, package = Package)
             d = eval(parse(text = Item))
      
             classes = if (sum(class(d) %in% c('data.frame')) > 0) unlist(lapply(d, class))
                       else NA_integer_
      
             .(class    = paste(class(d), collapse = ","),
               nrow     = if (!is.null(nrow(d))) nrow(d) else NA_integer_,
               ncol     = if (!is.null(ncol(d))) ncol(d) else NA_integer_,
               charCols = sum(classes == 'character'),
               facCols  = sum(classes == 'factor'))
           }
         , by = .(Package, Item)]
      #      Package          Item                                               class nrow ncol charCols facCols
      #  1: datasets AirPassengers                                                  ts   NA   NA       NA      NA
      #  2: datasets       BJsales                                                  ts   NA   NA       NA      NA
      #  3: datasets  BJsales.lead                                                  ts   NA   NA       NA      NA
      #  4: datasets           BOD                                          data.frame    6    2        0       0
      #  5: datasets           CO2 nfnGroupedData,nfGroupedData,groupedData,data.frame   84    5        0       3
      # ---                                                                                                      
      #492: survival    transplant                                          data.frame  815    6        0       3
      #493: survival        uspop2                                               array  101    2       NA      NA
      #494: survival       veteran                                          data.frame  137    8        0       1
      #495:  viridis   viridis.map                                          data.frame 1024    4        1       0
      #496:   xtable           tli                                          data.frame  100    5        0       3
      

      【讨论】:

      • 仅供参考,我已将其改造成我将使用的功能,请参阅我更新后的答案。
      【解决方案3】:

      在包datasets 中没有满足您条件的data.frame 类数据集,更准确地说,如果它们属于data.frame 类并且最多有100 列,那么它们都没有两列或更多列character。我刚刚通过以下代码的第一个版本发现了这一点。

      library(datasets)
      res <- library(help = "datasets")
      
      dat <- unlist(lapply(strsplit(res$info[[2]], " "), '[[', 1))
      dat <- dat[dat != ""]
      df_names <- NULL
      for(i in seq_along(dat)){
          d <- tryCatch(get(dat[i]), error = function(e) e)
          if(inherits(d, "data.frame")){
              if(nrow(d) <= 100){
                  char <- sum(sapply(d, is.character))
                  fact <- sum(sapply(d, is.factor))
                  if(char >= 2 || fact >= 2){
                      print(dat[i])
                      df_names <- c(df_names, dat[i])
                  }
              }
          }
      }
      
      df_names
      [1] "CO2"        "esoph"      "npk"        "sleep"      "warpbreaks"
      

      所以我必须包含额外的指令来处理factor 类的列。默认情况下,数据框使用stringsAsFactors = TRUE 创建。如果你能处理这些,你就有了,它们的名字在矢量df_names 中。为了使它们在全球环境中可用,只需 get 您想要的。

      【讨论】:

      • 很好,谢谢。我想如果没有内置任何东西,我会围绕它构建一个通用功能并在这里分享。像一些带有数据集名称、描述、类、长度、每个类的项目数的 data.frame。还有一个data 函数可以返回数据集,您可以将其限制在某些包中,使用它可能会很有趣。但令我惊讶的是,我们看到的每个涉及日期的示例都是一个人随机浏览 100 个数据集列表的结果,或者像你一样编写自定义函数。
      【解决方案4】:

      myfun()返回的表可以用适当的条件过滤,数据集的列可以通过类列中给定的类来标识。

      caret 包的问题在于它没有任何数据框或矩阵对象。数据集可能存在于列表对象内的caret 中。我不确定,caret 包中的一些列表对象包含函数列表。

      另外,如果有兴趣,您可以使myfun() 函数更具体地返回有关数据帧或矩阵对象的信息。

      myfun <- function( package )
      {
        t( sapply( ls( paste0( 'package:', package ) ), function(x){
          y <- eval(parse(text = paste0( package, "::`", x, "`")))
          data.frame( data_class = paste0(class(y), collapse = ","), 
                      nrow = ifelse( any(class(y) %in% c( "data.frame", "matrix" ) ),
                                     nrow(y), 
                                     NA_integer_ ),
                      ncol = ifelse( any(class(y) %in% c( "data.frame", "matrix" ) ),
                                     ncol(y),
                                     NA_integer_),
                      classes = ifelse( any(class(y) %in% c( "data.frame", "matrix" ) ),
                                        paste0( unlist(lapply(y, class)), collapse = "," ),
                                        NA),
                      stringsAsFactors = FALSE )
      
        } ) )
      }
      
      library( datasets )
      meta_data <- myfun( package = "datasets")
      head(meta_data)
      #               data_class   nrow ncol classes                                                          
      # ability.cov   "list"       NA   NA   NA                                                               
      # airmiles      "ts"         NA   NA   NA                                                               
      # AirPassengers "ts"         NA   NA   NA                                                               
      # airquality    "data.frame" 153  6    "integer,integer,numeric,integer,integer,integer"                
      # anscombe      "data.frame" 11   8    "numeric,numeric,numeric,numeric,numeric,numeric,numeric,numeric"
      # attenu        "data.frame" 182  5    "numeric,numeric,factor,numeric,numeric"  
      
      meta_data[ "ChickWeight", ]
      # $data_class
      # [1] "nfnGroupedData,nfGroupedData,groupedData,data.frame"
      # 
      # $nrow
      # [1] 578
      # 
      # $ncol
      # [1] 4
      # 
      # $classes
      # [1] "numeric,numeric,ordered,factor,factor"
      
      library( 'caret' )
      meta_data <- myfun( package = "caret")
      #               data_class nrow ncol classes
      # anovaScores   "function" NA   NA   NA     
      # avNNet        "function" NA   NA   NA     
      # bag           "function" NA   NA   NA     
      # bagControl    "function" NA   NA   NA     
      # bagEarth      "function" NA   NA   NA     
      # bagEarthStats "function" NA   NA   NA 
      

      如果加载的包需要在对包应用myfun()函数后卸载,试试这个:

      loaded_pkgs <- search()
      library( 'caret' )
      meta_data <- myfun( package = "caret")
      unload_pkgs <- setdiff( search(), loaded_pkgs )
      for( i in unload_pkgs ) { 
        detach( pos = which( search() %in% i ) ) 
      }
      

      【讨论】:

      • 我真的很喜欢使用ls('package:...') 的想法,因为它可以访问其他对象,这可以用来做更酷的事情,比如通过正则表达式查找函数或进行更多工作查找例如,按参数 up 函数。但它没有“看到”某些数据集是有问题的,例如来自caret 包的数据集。
      猜你喜欢
      • 2016-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-23
      • 2016-11-13
      • 2014-09-14
      • 1970-01-01
      • 2012-02-14
      相关资源
      最近更新 更多