![根据 Bash 中另一个文件的两个值调整一个 CSV 文件的行](https://linux22.com/image/224835/%E6%A0%B9%E6%8D%AE%20Bash%20%E4%B8%AD%E5%8F%A6%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E7%9A%84%E4%B8%A4%E4%B8%AA%E5%80%BC%E8%B0%83%E6%95%B4%E4%B8%80%E4%B8%AA%20CSV%20%E6%96%87%E4%BB%B6%E7%9A%84%E8%A1%8C.png)
我有两个 CSV 文件。
- 一种是长列:
Chr_Name
,h
,j
,start_pos
,end_pos
。 - 另一个是短文件,包括列
Chr_Name
和position
.
我需要根据第一个文件过滤Chr_Name
第一个文件中的行,然后根据第一个文件的开始位置和结束位置之间的位置过滤行。
我怎样才能做到这一点?
例子:
- 文件一:
Chrk, 10, 20, 1010, 1025 Chrk, 20, 10, 1020, 1040 ChrM, 10, 10, 50, 120
- 文件2:
Chrk, 1030 ChrM, 70
- 所需的输出文件:
Chrk, 20, 10, 1020, 1040 ChrM, 10, 10, 50, 120
答案1
假设您有两个 CSV 文件,如下所示:
$ cat file1
Chr_Name,h,j,start_pos,end_pos
Chrk,10,20,1010,1025
Chrk,20,10,1020,1040
ChrM,10,10,50,120
$ cat file2
Chr_Name,position
Chrk,1030
ChrM,70
您可以使用磨坊主(mlr
) 到加入共同字段上的两个文件Chr_Name
,筛选position
通过仅提取字段介于start_pos
和之间的记录来获取结果数据end_pos
,最后切position
数据中不需要的字段。
$ mlr --csv join -f file2 -j Chr_Name then filter '$start_pos <= $position && $position <= $end_pos' then cut -x -f position file1
Chr_Name,h,j,start_pos,end_pos
Chrk,20,10,1020,1040
ChrM,10,10,50,120
该mlr
命令的格式很好:
mlr --csv \
join -f file2 -j Chr_Name then \
filter '$start_pos <= $position && $position <= $end_pos' then \
cut -x -f position \
file1
使用与上面相同的两个文件,但使用 SQLite3 和内存数据库正如 Marcus Müller 在评论中所建议的:
$ sqlite3 :memory: '.mode csv' '.headers on' '.import file1 file1' '.import file2 file2' 'SELECT file1.* FROM file1 JOIN file2 ON (file1.Chr_Name = file2.Chr_name) WHERE CAST(position AS INTEGER) BETWEEN start_pos AND end_pos'
Chr_Name,h,j,start_pos,end_pos
Chrk,20,10,1020,1040
ChrM,10,10,50,120
SQLite3语句:
.mode csv
.headers on
.import file1 file1
.import file2 file2
SELECT file1.* FROM file1
JOIN file2 ON (file1.Chr_Name = file2.Chr_name)
WHERE CAST(position AS INTEGER) BETWEEN start_pos AND end_pos
虚线命令将两个文件导入到表中file1
,而file2
语句则SELECT
执行实际查询。
答案2
使用任何 awk,无论 s 之后是否确实有空格,
:
$ awk -F', *' 'NR==FNR{a[$1]=$2; next} ($1 in a) && ($4 <= a[$1]) && (a[$1] <= $5)' File2 File1
Chrk, 20, 10, 1020, 1040
ChrM, 10, 10, 50, 120