【问题标题】:Recreate vector from print() console output从 print() 控制台输出重新创建向量
【发布时间】:2018-07-24 08:29:40
【问题描述】:

遗憾的是,您经常看到有关 SO 的问题以某种格式呈现数据 这是不可重现的;通常只是 print() 的复制结果 ...

set.seed(1)

x <- sample(LETTERS, 40, replace = T)
y <- rnorm(20)

...比如这样:

x
 [1] "G" "J" "O" "X" "F" "X" "Y" "R" "Q" "B" "F" "E" "R" "J" "U" "M" "S"
[18] "Z" "J" "U" "Y" "F" "Q" "D" "G" "K" "A" "J" "W" "I" "M" "P" "M" "E"
[35] "V" "R" "U" "C" "S" "K"

...或者这个:

y
 [1]  0.91897737  0.78213630  0.07456498 -1.98935170  0.61982575
 [6] -0.05612874 -0.15579551 -1.47075238 -0.47815006  0.41794156
[11]  1.35867955 -0.10278773  0.38767161 -0.05380504 -1.37705956
[16] -0.41499456 -0.39428995 -0.05931340  1.10002537  0.76317575

理想情况下,我希望能够将文本从上面的块复制到我的剪贴板,并调用一些函数foo(),这样all.equal(foo(), x) 用于离散数据类型,all(near(foo(), y)) 用于浮点数(考虑到打印的准确性)。

有没有一种简单的方法可以(近似地)从print()'ing 的复制结果中重建一个简单的向量?


编辑:具有讽刺意味的是,我意识到我自己的示例并非完全可重现。这是创建复制的打印输出的代码:
y_printed <- capture.output(y)

【问题讨论】:

  • 我可以想象一个简单的实现不会太难,所以虽然这样的实现当然是受欢迎的,但我更想知道这个功能是否已经在发布的包中可用。
  • 如果有这样的功能并将其链接到插件、热键或狙击手,那就太好了。一些功能是日期检测,尝试用多个空格分隔以处理包含空格的字符值,检测是否有列标题,删除开始一行的#字符,支持列表输出等...包datapasta对于您从网上复制粘贴的表格有类似的东西,但上次我检查它在 SO 表格上效果不佳。
  • @Moody_Mudskipper 是的,这很有用。考虑到软件包,似乎这样的东西非常适合增强 datapasta

标签: r


【解决方案1】:

我使用scan 来解决这个问题。

你能用下面的代码做一个函数吗?

y <-
  '[1]  0.91897737  0.78213630  0.07456498 -1.98935170  0.61982575
 [6] -0.05612874 -0.15579551 -1.47075238 -0.47815006  0.41794156
[11]  1.35867955 -0.10278773  0.38767161 -0.05380504 -1.37705956
[16] -0.41499456 -0.39428995 -0.05931340  1.10002537  0.76317575'

y <- scan(what = character(), text = y)
y <- sub("^\\s*\\[\\d+\\]", "", y)
y <- as.numeric(y[y != ""])

根据@Moody_Mudskipper 评论中的建议,

模式可以更新为 "^\s*\[\d+\]" 以支持 OP 的示例(以空格开头)。

一个函数可以是

recreateVector <- function(X, numeric = TRUE, quiet = FALSE){
  X <- scan(what = character(), text = X, quiet = quiet)
  X <- sub("^\\s*\\[\\d+\\]", "", X)
  X <- X[X != ""]
  if(numeric) X <- as.numeric(X)
  X
}


recreateVector(y)   # Use the original y
#Read 24 items
# [1]  0.91897737  0.78213630  0.07456498 -1.98935170  0.61982575
# [6] -0.05612874 -0.15579551 -1.47075238 -0.47815006  0.41794156
#[11]  1.35867955 -0.10278773  0.38767161 -0.05380504 -1.37705956
#[16] -0.41499456 -0.39428995 -0.05931340  1.10002537  0.76317575

使用字符向量,设置参数numeric = FALSE,默认为TRUE

x <-
'[1] "G" "J" "O" "X" "F" "X" "Y" "R" "Q" "B" "F" "E" "R" "J" "U" "M" "S"
[18] "Z" "J" "U" "Y" "F" "Q" "D" "G" "K" "A" "J" "W" "I" "M" "P" "M" "E"
[35] "V" "R" "U" "C" "S" "K"'

recreateVector(x, numeric = FALSE)
#Read 43 items
# [1] "G" "J" "O" "X" "F" "X" "Y" "R" "Q" "B" "F" "E" "R" "J" "U"
#[16] "M" "S" "Z" "J" "U" "Y" "F" "Q" "D" "G" "K" "A" "J" "W" "I"
#[31] "M" "P" "M" "E" "V" "R" "U" "C" "S" "K"

注意参数quiet。我已将默认值设置为 FALSE,就像在 scan 的定义中一样,因为我更喜欢查看是否实际读入了任何内容。

【讨论】:

  • 我觉得数字参数有点毫无意义,因为除了简单的字符串或数字之外,还有更多的选项。不过很好的解决方案!
  • 我喜欢:scan() 是我的新朋友!不过,一些启发式方法肯定对猜测类型很有用。
  • 我相信data.table::fread 猜到了类型,也许可以调整解决方案以利用它。模式可以更新为 "^\\s*\\[\\d+\\]" 以支持 OP 的示例(以空格开头)。
【解决方案2】:

我们可以模仿读取 CSV 文件时对数据类型所做的猜测:

library(tidyverse)
unprint <- function(s) {
  s %>% str_replace_all(" *\\[\\d+\\] *","") %>% str_replace_all(" +","\n") %>% 
  textConnection %>% read.table
}
unprint(' [1]  0.91897737  0.78213630  0.07456498 -1.98935170  0.61982575
 [6] -0.05612874 -0.15579551 -1.47075238 -0.47815006  0.41794156
[11]  1.35867955 -0.10278773  0.38767161 -0.05380504 -1.37705956
[16] -0.41499456 -0.39428995 -0.05931340  1.10002537  0.76317575') %>% head

#           V1
#1  0.91897737
#2  0.78213630
#3  0.07456498
#4 -1.98935170
#5  0.61982575
#6 -0.05612874


unprint(' [1] "G" "J" "O" "X" "F" "X" "Y" "R" "Q" "B" "F" "E" "R" "J" "U" "M" "S"
[18] "Z" "J" "U" "Y" "F" "Q" "D" "G" "K" "A" "J" "W" "I" "M" "P" "M" "E"
[35] "V" "R" "U" "C" "S" "K"') %>% head

#  V1
#1  G
#2  J
#3  O
#4  X
#5  F
#6  X

处理字符串中括号的更详细的版本: 还给出了正确的输出:一个向量,而不是一个数据框。

unprint <- function(s) {
  t <- s %>% textConnection %>% readLines %>% 
    str_replace(" *\\[\\d+\\] *","") %>%
    paste(collapse=' ') %>% str_replace_all(" ","\n") %>% 
    textConnection %>% read.table(stringsAsFactors=FALSE) 
  t$V1 %>% str_replace_all("\n"," ")
}

x <- unprint(' [1] "x + y  [1]" "x + z  [2]"')
x
#[1] "x + y  [1]" "x + z  [2]"

【讨论】:

  • 我认为在正则表达式中使用\\s 更明确一些
  • 类型猜测绝对有用;字符串替换让我有点担心,例如(一个稍微做作的例子)unprint('[1] "x + y [1]"')
【解决方案3】:

为了我的使用,我最终修改了@RuiBarradas 的答案以包含 我想要的一些功能:从剪贴板中读取,并输入猜测(使用 readr 的帮助)。

rescue_vector <- function(x = readClipboard()) {
  x <- gsub("(^|\n)\\s*\\[\\d+\\]", "", x)
  x <- scan(text = x, what = character(),
            allowEscapes = TRUE, quiet = TRUE)
  readr::parse_guess(x, na = character())
}

它适用于给定的示例数据:

set.seed(1)

x <- sample(LETTERS, 40, replace = TRUE)
all.equal(x, rescue_vector(capture.output(x)))
#> [1] TRUE

y <- rnorm(20)
all.equal(y, rescue_vector(capture.output(y)))
#> [1] TRUE

并从剪贴板中读取:

writeClipboard(capture.output(y))
all.equal(y, rescue_vector())
#> [1] TRUE

还有一些奇怪的案例:

z <- c("[1] first \n second", "[2] + 1")
all.equal(z, rescue_vector(capture.output(z)))
#> [1] TRUE

但缺失值仍然是个问题:

na <- c("", "NA", NA)
rescue_vector(capture.output(na))
#> [1] "" NA NA

正如@Moody_Mudskipper 在 cmets 中提到的,进一步的发展可能 也包括对粘贴表的救援尝试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    • 2013-11-14
    • 1970-01-01
    • 2016-02-03
    • 2019-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多