在 R 中压缩或枚举?

Posted

技术标签:

【中文标题】在 R 中压缩或枚举?【英文标题】:Zip or enumerate in R? 【发布时间】:2012-03-06 01:59:15 【问题描述】:

这些 Python 列表推导的 R 等价物是什么:

[(i,j) for i,j in zip(index, Values)]
[(i,j) for i,j in enumerate(Values)]
[(i,j) for i,j in enumerate(range(10,20))]   %MWE, indexing or enumerating to 
                                            %keep up with the index, there may 
                                            %be some parameter to look this up

输出示例

>>> [(i,j) for i,j in enumerate(range(10,20))]
[(0, 10), (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), (6, 16), (7, 17), (8, 18), (9, 19)]

我之前已经用 R 中的一些技巧解决了这个问题,但现在不记得了,第一个想法是 itertools -pkg 但我希望找到一种更惯用的做事方式。

【问题讨论】:

如果你能为我们这些不熟悉 Python 的人提供一个小的工作示例,它可能会增加潜在回答者的数量。我猜最后一个是expand.grid(i=10:20,j=10:20) @BenBolker:添加了一个输出——现在清楚了吗?这可能更具挑战性,但逻辑很重要...... 我同意@DWin。期望 R 和 Python 中的数据结构之间存在一对一的映射是不合理的。如果你想要好的答案,你应该指定你希望结果在 R 中而不是在 Python 中的外观。 顺便说一句,这里有一个压缩和展平两个列表的巧妙方法:as.vector(rbind(1:10, 11:20)) 【参考方案1】:

pythonenumerate的答案:

在 R 中,列表是有序的(参见 this answer)。因此,您只需要索引键(使用names()[i])或值(使用[[i]])。

使用seq_along(也可以使用for(i in 1:length(mylist))...):

> mylist <- list('a'=10,'b'=20,'c'=30)
> for (i in seq_along(mylist))
+   print(paste(i,names(mylist)[i],mylist[[i]]))
+ 
[1] "1 a 10"
[1] "2 b 20"
[1] "3 c 30"

pythonzip的答案:

请参阅上述答案之一以模仿元组列表。我更喜欢 BondedDust 的回答中显示的数据框:

> x <- 1:3
> y <- 4:6
> data.frame(x=x, y=y)
  x y
1 1 4
2 2 5
3 3 6

【讨论】:

继续你的第一个例子到第二个,data.frame(names=labels(mylist),values=unlist(mylist),row.names = 1:length(mylist)) 一个关于性能的问题:调用 names(mylist)[i] 是否每次都必须做工作,还是一个微不足道的操作?我想知道是否最好在循环之前将其分配给 name_list R 语法太丑了,如果我们想做一些超出正常语法范围的事情,这种语言在某些地方是那么不灵活,但其他地方很棒(例如 NSE 和 SE)【参考方案2】:

围绕 R 的列表理解进行了一些讨论,例如here 或 there。 hash 包甚至提供了类似字典的结构。然而,正如其他人所说,如果没有清楚地了解它应该用于什么,就很难尝试将一种语言设施映射到另一种语言设施(即使这是 Comparison of programming languages 实际提供的)。例如,我可以在 R 中模仿 Python zip(),如下所示:

Python

In [1]: x = [1,2,3]
In [2]: y = [4,5,6]
In [3]: zip(x, y)
Out[3]: [(1, 4), (2, 5), (3, 6)]

R

> x <- 1:3
> y <- 4:6
> list(x, y)                     # gives a simple list
> as.list(paste(x, y))           # three tuples, as a list of characters
> mapply(list, x, y, SIMPLIFY=F) # gives a list of 3 tuples
> rbind(x, y)                    # gives a 2x3 matrix 

可以看出,这实际上取决于您之后想要对结果做什么。

【讨论】:

我认为问题是当你在 python 中使用 zip 时你会使用什么。典型用途是使用多个参数进行列表推导,因此 mapply 直接处理。 mapply 是我们想要的直接模拟。 @javadba mapply 涵盖了最常见的用例:zip,然后是 map。【参考方案3】:

zipenumerate 在 R 中实现起来并不是特别困难:

#' zip(1:5,1:10)
zip <- function(...) 
  mapply(list, ..., SIMPLIFY = FALSE)

枚举很容易用zip定义:

#' enumerate(l=LETTERS)
enumerate <- function(...) 
  zip(ix=seq_along(..1), ...)

由于这些是正确的函数,我们可以使用... 使其相当灵活和简洁,并利用 mapply 的行为,例如回收输入和正确命名输出。

【讨论】:

这些已添加到*** 包中,fwiw。 感谢您告诉我这件事,但我在文档中没有看到它:cran.r-project.org/web/packages/***/***.pdf 如何修改 enumerate 以创建默认变量名?我希望索引为 k,值为 v。现在我这样做:enumerate &lt;- function(...)zip(k = seq_along(..1), ...)enumerate(v = LETTERS)。我希望 v 成为默认值,但是如何使用默认变量更改函数? 还有什么方法可以让这个数据框工作吗?默认给我数据框列,但我宁愿枚举行。目前我使用enumerate(v = split(g, seq(nrow(g))))g = data.frame(a = c(1, 2, 3), b = c(4, 5, 6)) 在 cran 构建中它被命名为 zip2 以不与基础 R 冲突。【参考方案4】:

另一个创建向量列表的选项是使用 @peterhurford 在这里看到的 Map 函数:https://rdrr.io/github/peterhurford/funtools/src/R/zippers.R

> x <- 1:3
> y <- 4:6
> z <- 7:9
> Map(c, x, y, z)
[[1]]
[1] 1 4 7

[[2]]
[1] 2 5 8

[[3]]
[1] 3 6 9

【讨论】:

在 Python 中,zip 的主要用途是迭代多个向量/列表:for xi, yi in zip(x, y): ...。 +1 是迄今为止我在 R 中看到的最优雅的解决方案:for (xi.yi in Map(c, x, y)) xi &lt;- xi.yi[1]; yi &lt;- xi.yi[2]; ... 【参考方案5】:

如果那是矩阵的 Python 打印表示,那么这段代码:

j <- 10:20
matrix(c(seq_along(j), j), ncol=2)
#------------
      [,1] [,2]
 [1,]    1   10
 [2,]    2   11
 [3,]    3   12
 [4,]    4   13
 [5,]    5   14
 [6,]    6   15
 [7,]    7   16
 [8,]    8   17
 [9,]    9   18
[10,]   10   19
[11,]   11   20

你仍然让我们这些不是 Python 用户的人对你想要的输出结构一无所知。您使用术语“列表”,但输出表明一组有序的元组。

鉴于@chi 的指导,我们可能还建议使用非常以 R 为中心的“数据框”结构

x <- 1:3
y <- 4:6
dfrm <- data.frame(x=x, y=y)

...它在列类型方面具有列表的灵活性,在行和列索引方面具有矩阵的访问特性。或者可以使用 hhh 的请求并创建 j 向量 10:20 的隐式索引值,使用默认从“1”开始的 rownames 向量,但可以将其更改为从“开始”的字符向量0"

dfrm <- data.frame(j=10:20)
dfrm[3, ]
#[1] 12

 rownames(dfrm) <- 0:10
 dfrm["0",]
# [1] 10

不幸的是,粗心的人会发现 dfrm[0, ] 不是一个愉快的调用,返回长度为 0 的向量。

【讨论】:

+1 以获得优雅的解决方案。 (不,这些不是 Python 矩阵,但正如您已经猜到的那样 list of tuples。)【参考方案6】:

为了将 Python 样式的列表推导与枚举(例如枚举列表)一起使用,一种方法是安装 List-comprehension 包 LC(2018 年开发)和 itertools 包(2015 年开发)。

R 中的列表推导

您可以找到LC 包here。

install.packages("devtools")
devtools::install_github("mailund/lc")

例子

> library(itertools); library(lc)
> lc(paste(x$index, x$value), x=as.list(enumerate(rnorm(5))), )
[[1]]
[1] "1 -0.715651978438808"

[[2]]
[1] "2 -1.35430822605807"

[[3]]
[1] "3 -0.162872340884235"

[[4]]
[1] "4 1.42909760816254"

[[5]]
[1] "5 -0.880755983937781"

其中的编程语法还没有 Python 中那样干净和优美,但在功能上可以正常工作及其帮助概述:

"语法如下: lc(expr, lists, predicates) 其中 expr 是要对列表中的所有元素求值的某个表达式,其中 列表是一个或多个命名列表,其中这些列表由名称指定 和一个表达式名称 = list_expr,其中谓词是 应计算为布尔值的表达式。例如,要 从我们可以写的列表 x 中获取所有偶数的平方列表 lc(x ** 2, x = x, x %% 2 == 0)。调用 lc 的结果是一个列表 由 expr 中的表达式构造,用于 谓词计算结果为 true 的输入列表。"

请注意,例如在上面的示例中,您可以将谓词留空。

Python 风格的迭代工具和枚举

您可以使用与 Python 的 itertools 非常相似的 R 的 itertools,进一步在 Cran here

library(itertools)

描述的地方

“用于创建迭代器的各种工具,许多模仿 Python itertools 模块中的函数,还有一些模仿函数 在‘雪’包中。”

示例。枚举

> for (a in as.list(enumerate(rnorm(5))))  print(paste(a$index, "index:", a$value))
[1] "1 index: 1.63314811372568"
[1] "2 index: -0.983865948988314"
[1] "3 index: -1.27096072277818"
[1] "4 index: 0.313193212706331"
[1] "5 index: 1.25226639725357"

示例。 ZIP 枚举

> for (h in as.list(izip(a=1:5, b=letters[1:5])))  print(paste(h$a, "index:", h$b))
[1] "1 index: a"
[1] "2 index: b"
[1] "3 index: c"
[1] "4 index: d"
[1] "5 index: e"

【讨论】:

【参考方案7】:
# similar to python. return a list of list. Short sequences get recycled.
zip <- function(...) 
    all.list <- list(...)
    ele.names <- names(all.list)
    max.length <- max(sapply(all.list, length))
    lapply(0:(max.length - 1), function(i) 
        res <- lapply(all.list, function(l) l[i %% length(l) + 1]) 
        names(res) <- ele.names
        res
    )

【讨论】:

请描述这段代码的作用。 这个函数和@chl指出的“mapply(list, x, y, SIMPLIFY=F)”做的完全一样【参考方案8】:

这可以使用两个粘贴语句来实现:

str1 <- paste(1:11, 10:20, sep=",", collapse='), (')
paste("(", str1, ")", sep = "")

输出如下:

'(1,10), (2,11), (3,12), (4,13), (5,14), (6,15), (7,16), (8,17), (9,18), (10,19), (11,20)'

【讨论】:

【参考方案9】:

对于 python,R 中的“枚举”等效项。将向量存储在列表中并使用索引对其进行迭代应该可以正常工作。

vect1 <- c('A', 'B', 'C')
vect2 <- c('a', 'b', 'c')

# eqiv to zip values:
idx_list <- list(vect1, vect2)
idx_vect <- c(1:length(idx_list[[1]]))

for(i in idx_vect)
    x <- idx_list[[1]][i]
    j <- idx_list[[2]][i]
    print(c(i, x, j))

输出:

[1] "1" "A" "a"
[1] "2" "B" "b"
[1] "3" "C" "c"

R 'list' 是一个很好的银行,用于存放向量并使用索引保留。

【讨论】:

以上是关于在 R 中压缩或枚举?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Windows的内置功能从脚本中压缩或解压缩?

如何在 laravel 5 中压缩 HTML

如何在Mac中压缩文件

如何在 C# 中压缩(和解压缩)字节 []?

bzoj 1068: [SCOI2007]压缩区间dp

在java中压缩和解压缩7z文件