使用 R 将大型 jsonl 文件读入 SQLite 数据库
Reading a large jsonl file into an SQLite database using R
这是我的第一个问题,请见谅!
我正在尝试访问 54GB .jsonl 文件中的 Twitter 数据。我无论如何都不是程序员,因此非常感谢替代解决方案、意见或建议。
在我目前的尝试中,我尝试使用 stream_in()
.
将文件直接流式传输到 SQLite 数据库中
library(DBI)
library(jsonlite)
library(RSQLite)
#I created the sqlite database
tweetdb <- dbConnect(RSQLite::SQLite(), "tweet.sqlite")
dbDisconnect(tweetdb)
unlink("tweetdb.sqlite")
#now I'm trying to stream data directly into the database
dbWriteTable(tweetdb, "all_tweets", stream_in(file("all_tweet_ids.jsonl")))
该代码似乎有效,但我确信有更好、更快的方法来执行此操作。目前,RStudio 说 opening file input connection. Found 870000 records...
在相当快地上升到大约 720000 条记录后,它停留在该数字大约一个小时,然后转到 870000,现在再次卡住。
我的最终目标是拥有一个数据库,data.frame,data.table,或数据的任何表示,以便我可以访问其中的一部分,构建子集,操作它,并工作用它。 SQL 数据库将是可取的 - 我相信 - 因为我正在使用 MacBook 并且我无法获得更多的计算能力。我正在努力对这些推文进行文本分析。
我想知道您是否 运行 遇到了内存问题。因为您使用的是 stream_in
,我将假设(这是此答案的要求)数据采用 ndjson 格式(换行符分隔 json)。这样,您可以将文件拆分为多个较小的文件(也许只是两个文件),然后单独读取它们,从而允许垃圾收集(内存管理)工作。
命令行(不是 R)split
会将一个文本文件拆分为多个文件。它在我见过的每个 linux 系统上都可用;在 windows 上,在 Windows 的 Git 内;我在我的 mac 上找到了它(尽管我没有对它进行 R 开发)。
我将从一个简单的 10 行 ndjson 文件开始:
dat <- data.frame(num=1:10)
jsonlite::stream_out(dat) # this will be the single file's contents
# {"num":1}
# {"num":2}
# {"num":3}
# {"num":4}
# {"num":5}
# {"num":6}
# {"num":7}
# {"num":8}
# {"num":9}
# {"num":10}
# Complete! Processed total of 10 rows.
jsonlite::stream_out(dat, file("quux.ndjson"))
# opening file output connection.
# Complete! Processed total of 10 rows.
# closing file output connection.
现在我可以将该文件拆分为(在本例中)两个文件:
list.files(pattern = "^quux.")
# [1] "quux.ndjson"
system2("split", c("--lines", "8", "quux.ndjson", "quux.ndjson."))
list.files(pattern = "^quux.")
# [1] "quux.ndjson" "quux.ndjson.aa" "quux.ndjson.ab"
system2("wc", c("-l", list.files(pattern = "^quux.")))
# 10 quux.ndjson
# 8 quux.ndjson.aa
# 2 quux.ndjson.ab
# 20 total
关于split
:
--lines
是单个文件中的最大行数。在您的情况下,您可能需要 500000 左右,但更小应该不会有什么坏处。
- 第一个
quux.ndjson
为输入文件名
- 第二个
quux.ndjson.
(注意后面的.
)是用于输出文件的前缀;如果你不提供这个,那么文件将被命名为 xaa
、xab
、xac
,...这主要是为了美观,使用你喜欢的任何前缀
然后我 运行 wc -l
只是为了显示每个文件的行数。不是必需的,只是示范性的。我们可以进一步证明拆分
readLines("quux.ndjson.ab")
# [1] "{\"num\":9}" "{\"num\":10}"
从这里开始,您应该能够遍历文件并做任何您想做的事情,也许
# note the updated pattern
for (fn in list.files(pattern = "^quux.ndjson.")) {
dbWriteTable(tweetdb, "all_tweets", stream_in(file(fn)))
}
(未经测试)。
由于我没有可用于测试的“大型”数据,因此有一个想法:根据执行此操作的频率,您可能 考虑调用 gc()
在每个 stream_in
之间。虽然它是自动的并且应该自己处理事情,但我想知道这是否会对您的流程产生任何影响。 gc
将强制进行垃圾回收,取消分配未引用的对象。我觉得没必要,只是一个想法。
这是我的第一个问题,请见谅!
我正在尝试访问 54GB .jsonl 文件中的 Twitter 数据。我无论如何都不是程序员,因此非常感谢替代解决方案、意见或建议。
在我目前的尝试中,我尝试使用 stream_in()
.
library(DBI)
library(jsonlite)
library(RSQLite)
#I created the sqlite database
tweetdb <- dbConnect(RSQLite::SQLite(), "tweet.sqlite")
dbDisconnect(tweetdb)
unlink("tweetdb.sqlite")
#now I'm trying to stream data directly into the database
dbWriteTable(tweetdb, "all_tweets", stream_in(file("all_tweet_ids.jsonl")))
该代码似乎有效,但我确信有更好、更快的方法来执行此操作。目前,RStudio 说 opening file input connection. Found 870000 records...
在相当快地上升到大约 720000 条记录后,它停留在该数字大约一个小时,然后转到 870000,现在再次卡住。
我的最终目标是拥有一个数据库,data.frame,data.table,或数据的任何表示,以便我可以访问其中的一部分,构建子集,操作它,并工作用它。 SQL 数据库将是可取的 - 我相信 - 因为我正在使用 MacBook 并且我无法获得更多的计算能力。我正在努力对这些推文进行文本分析。
我想知道您是否 运行 遇到了内存问题。因为您使用的是 stream_in
,我将假设(这是此答案的要求)数据采用 ndjson 格式(换行符分隔 json)。这样,您可以将文件拆分为多个较小的文件(也许只是两个文件),然后单独读取它们,从而允许垃圾收集(内存管理)工作。
命令行(不是 R)split
会将一个文本文件拆分为多个文件。它在我见过的每个 linux 系统上都可用;在 windows 上,在 Windows 的 Git 内;我在我的 mac 上找到了它(尽管我没有对它进行 R 开发)。
我将从一个简单的 10 行 ndjson 文件开始:
dat <- data.frame(num=1:10)
jsonlite::stream_out(dat) # this will be the single file's contents
# {"num":1}
# {"num":2}
# {"num":3}
# {"num":4}
# {"num":5}
# {"num":6}
# {"num":7}
# {"num":8}
# {"num":9}
# {"num":10}
# Complete! Processed total of 10 rows.
jsonlite::stream_out(dat, file("quux.ndjson"))
# opening file output connection.
# Complete! Processed total of 10 rows.
# closing file output connection.
现在我可以将该文件拆分为(在本例中)两个文件:
list.files(pattern = "^quux.")
# [1] "quux.ndjson"
system2("split", c("--lines", "8", "quux.ndjson", "quux.ndjson."))
list.files(pattern = "^quux.")
# [1] "quux.ndjson" "quux.ndjson.aa" "quux.ndjson.ab"
system2("wc", c("-l", list.files(pattern = "^quux.")))
# 10 quux.ndjson
# 8 quux.ndjson.aa
# 2 quux.ndjson.ab
# 20 total
关于split
:
--lines
是单个文件中的最大行数。在您的情况下,您可能需要 500000 左右,但更小应该不会有什么坏处。- 第一个
quux.ndjson
为输入文件名 - 第二个
quux.ndjson.
(注意后面的.
)是用于输出文件的前缀;如果你不提供这个,那么文件将被命名为xaa
、xab
、xac
,...这主要是为了美观,使用你喜欢的任何前缀
然后我 运行 wc -l
只是为了显示每个文件的行数。不是必需的,只是示范性的。我们可以进一步证明拆分
readLines("quux.ndjson.ab")
# [1] "{\"num\":9}" "{\"num\":10}"
从这里开始,您应该能够遍历文件并做任何您想做的事情,也许
# note the updated pattern
for (fn in list.files(pattern = "^quux.ndjson.")) {
dbWriteTable(tweetdb, "all_tweets", stream_in(file(fn)))
}
(未经测试)。
由于我没有可用于测试的“大型”数据,因此有一个想法:根据执行此操作的频率,您可能 考虑调用 gc()
在每个 stream_in
之间。虽然它是自动的并且应该自己处理事情,但我想知道这是否会对您的流程产生任何影响。 gc
将强制进行垃圾回收,取消分配未引用的对象。我觉得没必要,只是一个想法。