我知道线索如何在 R 中内部连接两个 csv 文件它有一个合并选项,但我不想要。我有两个 CSV 数据文件。我正在考虑如何像 SQL 和 R 一样进行查询。两个 CSV 文件,其中主键是data_id
。
data.csv
可以在log.csv
(等4
)中找到未找到的 ID
data_id, event_value
1, 777
1, 666
2, 111
4, 123
3, 324
1, 245
log.csv
其中列中没有重复项,ID
但可以重复项name
data_id, name
1, leo
2, leopold
3, lorem
部分 PostgreSQL 语法的伪代码
- 让
data_id=1
- 分别显示来自
name
和的 和event_value
data.csv
log.csv
类似 PostgreSQL 部分 select 的伪代码
SELECT name, event_value
FROM data, log
WHERE data_id=1;
预期产出
leo, 777
leo, 666
leo, 245
R方法
file1 <- read.table("file1.csv", col.names=c("data_id", "event_value"))
file2 <- read.table("file2.csv", col.names=c("data_id", "name"))
# TODO here something like the SQL query
# http://stackoverflow.com/a/1307824/54964
我认为sqldf
在这里就足够的可能方法
sqldf
data.table
dplyr
PostgreSQL Schema 伪代码显示我尝试对 CSV 文件执行的操作
CREATE TABLE data (
data_id SERIAL PRIMARY KEY NOT NULL,
event_value INTEGER NOT NULL
);
CREATE TABLE log (
data_id SERIAL PRIMARY KEY NOT NULL,
name INTEGER NOT NULL
);
R:3.3.3
操作系统:Debian 8.7
相关:相关线程中的 PostgreSQL 方法如何在 PostgreSQL 上使用两个 CSV 文件/…进行 SELECT?
答案1
R 有许多带有 SQL 级别便利性的包。最方便的套餐是
dplyr(现代,通常速度是基本函数的 10-100 倍),具有 SQL 启发的命令,例如 group-by 和不同的连接
SparkR(如果您需要 Spark 支持,这里显然不是,但它附带了很好的 SQL 方便命令)以及受 SQL 启发的命令,例如 group-by 和不同的联接
data-table
提供比基本功能更有效的功能,例如替换read.csv
为fread
。SQLDF 非常不可靠且效率低下,是的,您会遇到致命错误,并出现优先级错误,就像上面失败的 Rstudio 一样。
这些软件包不会教您学习和调试 SQL:为了学习正确的 SQL,SQLDF 当然不是最方便的工具。 SQL Server Management Studio(Windows)可能更容易使用,例如与 Azure Basic SQL DB 一起使用,每月 5 欧元或免费套餐这里或其他一些数据库——或者自己设置数据库
- 使用命令源 postgres 数据库
src_postgres()
,有关 R 中数据库命令的更多信息这里
下面为您提供了演示,展示了正确的 SQL 并修复了代码案例中出现的错误。我还展示了 SQL 便利命令。最好先正确学习 SQL,这样您就知道在 R 包中寻找什么。
演示
代码 4 由于优先级错误而失败。 where 子句和 USING 必须位于连接之后。
> file1 <- read.csv("test1.csv", header=TRUE, sep=",") > file2 <- read.csv("test2.csv", header=TRUE, sep=",") > sqldf("SELECT event_value, name + FROM file1 + LEFT JOIN + (SELECT data_id, name + FROM file2 + WHERE data_id = 1) + USING(data_id) + WHERE data_id = 1") event_value name 1 777 leo 2 666 leo 3 245 leo
其他方式包含
正确的 LEFT-JOIN 方法
> df3 <- sqldf("SELECT event_value, name + FROM file1 a + LEFT JOIN file2 b ON a.data_id=b.data_id") > > df3 event_value name 1 777 leo 2 666 leo 3 111 leopold 4 123 <NA> 5 324 lorem 6 245 leo > df3 <- sqldf("SELECT a.event_value, b.name + FROM file1 a + LEFT JOIN file2 b ON a.data_id=b.data_id + WHERE a.data_id = 1") > df3 event_value name 1 777 leo 2 666 leo 3 245 leo
使用 where 条件合并表
> df4 <- sqldf("SELECT a.event_value, b.name + FROM file1 a, file2 b + WHERE a.data_id = 1 + AND a.data_id=b.data_id") > > df4 event_value name 1 777 leo 2 666 leo 3 245 leo
SQL子查询方法
> df5 <- sqldf("SELECT a.event_value, b.name + FROM + (SELECT data_id, event_value FROM file1) a, + (SELECT data_id, name FROM file2) b + WHERE a.data_id = 1 + AND a.data_id=b.data_id") > df5 a.event_value b.name 1 777 leo 2 666 leo 3 245 leo
使用 R 的 SQL 便捷方式有哪些?
SQL 风格的连接
build_sql 执行 SQL 风格命令的便捷函数
Case-when 函数的灵感来自于 SQL-CASE-WHEN
- Coalesce 函数受 SQL-COALESCE 启发
translate_sql 帮助将 R 函数转换为 SQL(更多情况这里)
# Many functions have slightly different names translate_sql(x == 1 && (y < 2 || z > 3)) #> <SQL> "x" = 1.0 AND ("y" < 2.0 OR "z" > 3.0) translate_sql(x ^ 2 < 10) #> <SQL> POWER("x", 2.0) < 10.0 translate_sql(x %% 2 == 10) #> <SQL> "x" % 2.0 = 10.0
SQLLite 和 dplyr:安装 sqlite 包并使用 dplyr 尝试 NYC 数据集,更多这里
SparkR包
带有 SQL 风格的连接(内连接、左连接等)和分组依据。更多的这里。
答案2
sqldf
方法。
一种方法显示了方法的警告-如果您通过 加入,则join
不能在两个表上使用。代码1WHERE data_id
data_id
file1 <- read.table("data.csv", col.names=c("data_id", "event_value"))
file2 <- read.table("log.csv", col.names=c("data_id", "name"))
library("sqldf")
df3 <- sqldf("SELECT event_value, name
FROM file1
LEFT JOIN file2 USING(data_id)")
df3
输出错误,因为data_id = 1
也应该处于活动状态
Loading required package: gsubfn
Loading required package: proto
Loading required package: RSQLite
Loading required package: tcltk
Warning message:
Quoted identifiers should have class SQL, use DBI::SQL() if the caller performs the quoting.
event_value name
1 event_value name
2 777 leo
3 666 leo
4 111 leopold
5 123 <NA>
6 324 lorem
7 245 leo
代码2
代码
df3 <- sqldf("SELECT event_value, name
FROM file1
LEFT JOIN file2 USING(data_id)
WHERE data_id = 1")
输出空白,因为join
已经应用
[1] event_value name
<0 rows> (or 0-length row.names)
代码3
WHERE
早点做
df3 <- sqldf("SELECT event_value, name
FROM file1
WHERE data_id = 1
LEFT JOIN file2 USING(data_id)")
输出错误,因为两个表的大小不同,因此WHERE
应该应用于两个表
Error in rsqlite_send_query(conn@ptr, statement) :
near "LEFT": syntax error
Calls: sqldf ... initialize -> initialize -> rsqlite_send_query -> .Call
In addition: Warning message:
Quoted identifiers should have class SQL, use DBI::SQL() if the caller performs the quoting.
Execution halted
代码4
使用两个SELECT
s 与JOIN
df3 <- sqldf("SELECT event_value, name
FROM file1
WHERE data_id = 1
LEFT JOIN
(SELECT data_id, name
FROM file2
WHERE data_id = 1)
USING(data_id)")
输出错误
Error in rsqlite_send_query(conn@ptr, statement) :
near "LEFT": syntax error
Calls: sqldf ... initialize -> initialize -> rsqlite_send_query -> .Call
In addition: Warning message:
Quoted identifiers should have class SQL, use DBI::SQL() if the caller performs the quoting.
Execution halted
SELECT
也许,第二个及其附件存在语法错误JOIN
。
答案3
我广泛使用珀尔语言模块文本::CSV_XS用于 CSV 文件的重型、临时操作。使用这个模块我构建了四个小的基本模块珀尔程序可以用作我想做的任何事情的构建块。
- Filter - 过滤输入文件过滤文件字段
- 拒绝 - 拒绝输入文件过滤器文件字段
- Stripper - Stripper 输入文件字段 [field2 field3…]
- Swap - 交换输入文件 swapFile matchField 外场
filterFile 的每一行都有一个正则表达式模式。任何与这些模式之一匹配的内容都会被匹配以达到接受或拒绝的目的。各种“字段”是列标题名称。
所以在你的例子中我只是把“1”放在filterFile中然后去:
perl Filter.pm data.csv filter.txt data_id >One.csv
perl Stripper.pm One.csv data_id event_value >Two.csv
perl Swap.pm Two.csv log.csv data_id name >Three.csv
如果我们还想要 Leopold 的事件,filter.txt 将有两行同名内容:
1
2
我有所有四个构建块例程的各种突变版本,它们执行诸如从 STDIN 获取输入或将输出发布到特定 URL 之类的操作。
如果您想直接使用专用例程来完成此操作,那将相当容易。 文本::CSV_XS轻松将 CSV 文件行提取到哈希中,然后您可以对它们执行您喜欢的操作。
首先,如果您的文件很大,您应该使用数据库文件模块指定您的哈希值应作为数据库存储在磁盘上。否则你可能会填满内存并陷入停顿。
use DB_File;
my %theHash;
unlink '/tmp/translation.db';
sleep 2;
tie ( %theHash, 'DB_File', '/tmp/translation.db' )
or die "Can't open /tmp/translation.db\n";
然后创建 CSV 对象
map{ $_ = Text::CSV_XS->new( { allow_whitespace => 1,
eol =>"\015\012",
always_quote => 1, binary => 1 })}
( $data_csv, $log_csv, $output_csv );
请注意,我使用的是 DOS EOL 字符。
然后拉入输入标题行以设置列名称
@cols = @{$data_csv->getline( $data_fh )};
$data_csv->column_names( @cols );
@cols = @{$log_csv->getline( $log_fh )};
$log_csv->column_names( @cols );
您在文件句柄 $data_fh 和 $log_fh 上打开文件的位置。
决定输出列是什么并写出列标题行
@output_cols = ( 'name', 'event_value' );
$output_csv->combine( @output_cols );
$latest_row = $output_csv->string();
print $output_fh, $latest_row;
然后组成一个 data_id 来命名哈希。
while ( $log_csv_row = $log_csv->getline_hr( $log_fh ) ){
$theHash{ $log_csv_row->{data_id} } = $log_csv_row->{name};
}
然后,如您的示例所示,循环遍历 data.csv 以获取所有“1”。
$outputHash{name} = $theHash{1};
while ( $data_csv_row = $data_csv->getline_hr( $data_fh ) ){
next unless $data_csv_row->{data_id} == 1;
$outputHash{data_id} = $data_csv_row->{data_id};
$output_csv->combine( map { $outputHash{$_} } @output_cols );
$latest_row = $output_csv->string();
print $output_fh "$latest_row";
}
此示例代码是上面列出的所有实用程序例程的基础,其中硬编码的“1”被替换为放入散列中的各种参数或参数数组。