【问题标题】:Change and save only one row in a file仅更改并保存文件中的一行
【发布时间】:2015-02-08 10:24:36
【问题描述】:

我想知道 R 中是否有什么东西可以让我更新文件而不是保存所有数据。

也许有类似sqldf::read.csv.sql 的东西可以保存。


好的

假设我将虹膜数据存储为 .csv:

 Sepal.Length Sepal.Width Petal.Length Petal.Width 种类
1 5.1 3.5 1.4 0.2 塞托萨
2 4.9 3.0 1.4 0.2 塞托萨
3 4.7 3.2 1.3 0.2 setosa

但我已经意识到,第二朵花是维吉尼亚,所以我想将第二行改为:

2 4.9 3.0 1.4 0.2 弗吉尼亚

我知道,我可以读取文件、更改 Species 并再次保存,但是文件中的行数越多(即 > 10 6),这种方法的效率就越低。

【问题讨论】:

  • 你能缩小你的问题范围吗?我们在谈论什么样的文件?对于文本文件,答案可能是否定的。
  • 写函数都有附加选项。同意这个问题太不清楚,无法支持编码响应。您应该编辑您的问题以包含更多细节。

标签: r csv flat-file sqldf


【解决方案1】:

一般来说,R 并不适用于就地文件编辑,而且我知道没有(当前可用的)工具在任何情况下都支持它。即使像sed 这样的unixy 工具也可以进行快速编辑,但在技术上仍然没有“就地”进行(即使它隐藏了它是如何做到的)。 (可能有一些这样做,但可能不是您想要的易于访问。)

有一个值得注意的例外,一种为就地编辑(嗯,交互)而设计的文件格式。它包括重要的就地添加、过滤、替换和删除运算符。在大多数情况下,它通常会这样做而无需增加文件大小。我是 SQLite

例如,

library(DBI)
# library(RSQLite) # don't need to load it, just need to have it available
fname <- "./iris.sqlite3"
con <- dbConnect(RSQLite::SQLite(), fname)
file.info(fname)$size
# [1] 0
dbWriteTable(con, "iris", iris)
# [1] TRUE
file.info(fname)$size
# [1] 16384
dbGetQuery(con, "select * from iris where [Sepal.Length]=4.7 and [Sepal.Width]=3.2 and [Petal.Length]=1.6")
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1          4.7         3.2          1.6         0.2  setosa
file.info(fname)$size
# [1] 16384
dbExecute(con, "update iris set [Species]='virginica' where [Sepal.Length]=4.7 and [Sepal.Width]=3.2 and [Petal.Length]=1.6")
# [1] 1
dbGetQuery(con, "select * from iris where [Sepal.Length]=4.7 and [Sepal.Width]=3.2 and [Petal.Length]=1.6")
#   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
# 1          4.7         3.2          1.6         0.2 virginica
dbDisconnect(con)
file.info(fname)$size
# [1] 16384

优点

  • cross-platform。它比大多数人意识到的要多产,是 Firefox 浏览器和 Android 操作系统的必需内部组件。 (Many others 也是。)
  • 此外,驱动程序存在于大多数编程语言中,包括 R、python、ruby 等等,这里不一一列举。
  • 对于可以存储在单个 SQLite 文件中的数据量实际上没有实际限制。理论上它最多支持 140TB (https://www.sqlite.org/whentouse.html),但如果你得到这么大的容量,那么对于不同的解决方案会有很多(合理的)论据。
  • 拉取数据建立在 SQL 标准之上,虽然不是 100% 兼容,但它是 pretty darn close。查询时间/性能取决于您的查询大小,但通常很快(参考:Will SQLite performance degrade if the database size is greater than 2 gigabytes?
  • 其实可以faster而不是单个文件操作。

缺点

  • 文件大小会有“开销”。值得注意的是iris 占用不到 7K 的内存(请参阅object.size(iris)),但文件大小从 16K 开始。对于更大的数据,差距ratio(文件大小与实际数据)会缩小。 (我对 ggplot2::diamonds 做了同样的事情;对象为 3456376 字节,文件大小为 3780608,不到 10% 大。)
  • 当 SQLite 认为有必要时,文件大小会增加。这是基于 R 和此问题/答案范围之外的许多因素。
  • 如果删除大量数据,文件大小不会立即减小以适应...请参阅change sqlite file size after "DELETE FROM table"(提示:vacuum
  • 有许多工具可以轻松/立即从此文件格式导入数据,但 Excel 和 Access 尤其缺席。使用SQLite-ODBC 是可行的,但需要一点肘部润滑脂才能做到。 (我可以接受,但并非所有用户都可以,而且一些企业网络使这一步变得困难或明确禁止。)

SQLite 文件作为 CSV

如果要全部导入,可以在导入时将其视为文件:

con <- dbConnect(RSQLite::SQLite(), fname)
iris2 <- dbGetQuery(con, "select * from iris")
dbDisconnect(con)

相比

iris2 <- read.csv("iris.csv", stringsAsFactors = FALSE)

如果你想变得花哨:

import_sqlite <- function(fname, tablename = NA) {
  if (length(tablename) > 1L) {
    warning("the condition has length > 1 and only the first element will be used")
    tablename <- tablename[[1L]]
  }
  con <- DBI::dbConnect(RSQLite::SQLite(), fname)
  on.exit(DBI::dbDisconnect(con), add = TRUE)
  available_tables <- DBI::dbListTables(con)
  if (length(available_tables) == 0L) {
    stop("no tables found")
  } else if (is.na(tablename)) {
    if (length(available_tables) == 1L) {
      tablename <- available_tables
    }
  }
  if (tablename %in% available_tables) {
    tablename <- DBI::dbQuoteIdentifier(con, tablename)
    qry <- sprintf("select * from %s", tablename)
    out <- tryCatch(list(data = DBI::dbGetQuery(con, DBI::SQL(qry)),
                         err = NULL),
                    error = function(e) list(data = NULL, err = e))
    if (! is.null(out$err)) {
      stop("[sqlite error] ", out$err$message)
    } else {
      return(out$data)
    }    
  } else {
    stop(sprintf("table %s not found", DBI::dbQuoteIdentifier(con, tablename)))
  }
}
head(import_sqlite("iris.sqlite3"))
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1          5.1         3.5          1.4         0.2  setosa
# 2          4.9         3.0          1.4         0.2  setosa
# 3          4.7         3.2          1.3         0.2  setosa
# 4          4.6         3.1          1.5         0.2  setosa
# 5          5.0         3.6          1.4         0.2  setosa
# 6          5.4         3.9          1.7         0.4  setosa

(除了概念验证之外,我没有提供该功能,您可以像与 CSV 一样与单个文件进行交互。其中有一些保护措施,但实际上只是对为了这个问题。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-14
    • 1970-01-01
    • 2019-11-15
    • 1970-01-01
    • 1970-01-01
    • 2014-06-09
    • 1970-01-01
    • 2010-10-07
    相关资源
    最近更新 更多