当前位置: 首页 > 知识库问答 >
问题:

如何在R中最好地结合独特和匹配?

娄鹤轩
2023-03-14

我发现自己经常编写代码,例如

#' @param x input vector
#' @param ... passed to [slow_fun()]
fast_fun <- function(x, ...) {
  u <- unique(x)
  i <- match(x, u)
  v <- slow_fun(u, ...)
  v[i]
}

加速一个缓慢的矢量化“纯”函数,其中每个输入项理论上可以单独计算,并且输入预计包含许多重复项。

现在我想知道这是否是实现这种加速的最佳方式,或者是否有一些函数(最好是在基本R或tidyverse中)同时执行诸如唯一匹配之类的操作?

谢谢你提供的答案。我编写了一个小型基准测试套件来比较这些方法:

method <- list(
  brute = slow_fun,
  unique_match = function(x, ...) {
    u <- unique(x)
    i <- match(x, u)
    v <- slow_fun(u, ...)
    v[i]
  },
  unique_factor = function(x, ...) {
    if (is.character(x)) {
      x <- factor(x)
      i <- as.integer(x)
      u <- levels(x)
    } else {
      u <- unique(x)
      i <- as.integer(factor(x, levels = u))
    }
    v <- slow_fun(u, ...)
    v[i]
  },
  unique_match_df = function(x, ...) {
    u <- unique(x)
    i <- if (is.numeric(x)) {
      match(data.frame(t(round(x, 10))), data.frame(t(round(u, 10))))
    } else {
      match(data.frame(t(x)), data.frame(t(u)))
    }
    v <- slow_fun(u, ...)
    v[i]
  },
  rcpp_uniquify = function(x, ...) {
    iu <- uniquify(x)
    v <- slow_fun(iu[["u"]], ...)
    v[iu[["i"]]]
  }
)

exprs <- lapply(method, function(fun) substitute(fun(x), list(fun = fun)))

settings$bench <- lapply(seq_len(nrow(settings)), function(i) {
  cat("\rBenchmark ", i, " / ", nrow(settings), sep = "")
  x <- switch(
    settings$type[i],
    integer = sample.int(
      n = settings$n_distinct[i],
      size = settings$n_total[i],
      replace = TRUE
    ),
    double = sample(
      x = runif(n = settings$n_distinct[i]),
      size = settings$n_total[i],
      replace = TRUE
    ),
    character = sample(
      x = stringi::stri_rand_strings(
        n = settings$n_distinct[i],
        length = 20L
      ),
      size = settings$n_total[i],
      replace = TRUE
    )
  )
  microbenchmark::microbenchmark(
    list = exprs
  )
})

library(tidyverse)
settings %>%
  mutate(
    bench = map(bench, summary)
  ) %>%
  unnest(bench) %>%
  group_by(n_distinct, n_total, type) %>%
  mutate(score = median / min(median)) %>%
  group_by(expr) %>%
  summarise(mean_score = mean(score)) %>%
  arrange(mean_score)

目前,基于rcpp的方法在我的机器上的所有测试设置中都是最好的,但几乎没有超过独特的然后匹配的方法。我怀疑x越长,性能上的优势就越大,因为unique-then-match需要对数据进行两次传递,而uniquify()只需要一次传递。

|expr            | mean_score|
|:---------------|----------:|
|rcpp_uniquify   |   1.018550|
|unique_match    |   1.027154|
|unique_factor   |   5.024102|
|unique_match_df |  36.613970|
|brute           |  45.106015|

共有3个答案

潘阳舒
2023-03-14

我终于设法击败独特()匹配()使用Rcpp在C中使用std::unordered_map作为核心簿记数据结构手工编码算法。

以下是源代码,可以通过将其写入文件并在其上运行Rcpp::sourceCpp在R中使用。

#include <Rcpp.h>
using namespace Rcpp;

template <int T>
List uniquify_impl(Vector<T> x) {
  IntegerVector idxes(x.length());
  typedef typename Rcpp::traits::storage_type<T>::type storage_t;
  std::unordered_map<storage_t, int> unique_map;
  int n_unique = 0;
  // 1. Pass through x once
  for (int i = 0; i < x.length(); i++) {
    storage_t curr = x[i];
    int idx = unique_map[curr];
    if (idx == 0) {
      unique_map[curr] = ++n_unique;
      idx = n_unique;
    }
    idxes[i] = idx;
  }
  // 2. Sort unique_map by its key
  Vector<T> uniques(unique_map.size());
  for (auto &pair : unique_map) {
    uniques[pair.second - 1] = pair.first;
  }
  
  return List::create(
    _["u"] = uniques,
    _["i"] = idxes
  );
}

// [[Rcpp::export]]
List uniquify(RObject x) {
  switch (TYPEOF(x)) {
  case INTSXP: {
    return uniquify_impl(as<IntegerVector>(x));
  }
  case REALSXP: {
    return uniquify_impl(as<NumericVector>(x));
  }
  case STRSXP: {
    return uniquify_impl(as<CharacterVector>(x));
  }
  default: {
    warning(
      "Invalid SEXPTYPE %d (%s).\n",
      TYPEOF(x), type2name(x)
    );
    return R_NilValue;
  }
  }
}
娄学文
2023-03-14

我最近找到了一个冷静、快速的答案,

匹配(数据帧(t(x)),数据。帧(t(y)))

和往常一样,使用浮动时要小心。我推荐这样的东西

匹配(data.frame(t(round(x,10))),数据。框架(t(圆形(y)))

在这种情况下。

方建明
2023-03-14

也许你可以试着把factor当作。整数如下所示

as.integer(factor(x))
 类似资料:
  • 我希望搜索具有类似于查询字符串的< code>firstField(映射为文本)或等于1的< code>secondField(映射为整数)的项目。 阅读文档后,我明白我应该使用和(如下所示): 然而,结果表明,对结果的贡献远远低于,因为它们的评分算法产生不同的量表。 我正在考虑人为地ing 。

  • 我正在写一个求解方程求解方法。方法将是递归的;搜索所有外括号,当找到时,重新求解括号内的值,当没有找到括号时返回值。 Regex.Replace(...)替换指定模式的所有匹配项。我希望能够匹配到多个场景,并用不同的输出替换每个场景

  • 本文向大家介绍如何在R中合并列表?,包括了如何在R中合并列表?的使用技巧和注意事项,需要的朋友参考一下 当我们有多个列表但它们具有相似的数据类型时,我们可能想要合并或合并这些列表。这将有助于使用,因为我们可以使用一个列表名称执行计算,而不是将其应用于多个列表名称。我们可以借助mapply函数组合多个列表。 示例 请看以下列表- 现在假设,我们还有一个列表,即List3,然后可以按照如下所示的相同方

  • 问题内容: 在另一个线程中,我表示我喜欢通过执行以下操作来居中GUI: 但是安德鲁·汤普森(Andrew Thompson)有不同的看法,而是打电话给 想问的人想知道为什么吗? 问题答案: 在我看来,屏幕中间的一个GUI看起来是这样的。我一直在等待它们消失,真正的GUI出现! 从Java 1.5开始,我们可以使用。哪一个.. 设置此窗口是否应在下一次使该窗口可见时显示在本机窗口系统的默认位置还是当

  • 问题内容: 我有一个Serializable对象,该对象应该将java.awt.Image作为其成员。我应该如何进行序列化? (抱歉,此版本是根据不太清晰的第一版编辑的。) 问题答案: ImageIcon实现了Serializable,可用于包装Image类 http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/ImageIcon.html

  • 当onPause发生时,我的线程没有停止,导致“线程已启动”logcat错误onResume,因为我无法运行两个线程实例。此时如何终止线程?我认为我需要做一些事情,比如: 但是我不能把它添加到我的气球基本暂停中,我认为上下文是错误的。所以请帮忙,代码如下所示。(代码示例会很有帮助,谢谢) 我的活动: 我的表面视图: 我的线程:公共类 GameLoopThreadBasic 扩展线程 { 私有游戏视