Соедините два CSV-файла, если значение находится между интервалами в файле 2 - PullRequest
0 голосов
/ 01 июля 2019

У меня есть два CSV-файла, к которым мне нужно присоединиться, F1 имеет миллионы строк, F2 (файл 1) имеет тысячи строк. Мне нужно объединить эти файлы, если позиция в файле F1 (F1.pos) находится между F2.start и F2.end. Есть ли способ, как это сделать в Bash? Потому что у меня есть код в Python pandas для sqllite3, и я ищу что-то быстрее.

Таблица F1 выглядит следующим образом:

| name  | pos   |
|------ |------ |
| a     | 1020  |
| b     | 1200  |
| c     | 1800  |

Таблица F2 выглядит следующим образом:

| interval_name     | start     | end   |
|---------------    |-------    |------ |
| int1              | 990       | 1090  |
| int2              | 1100      | 1150  |
| int3              | 500       | 2000  |

Результат должен выглядеть так:

| name  | pos   | interval_name     | start     | end   |
|------ |------ |---------------    |-------    |------ |
| a     | 1020  | int1              | 990       | 1090  |
| a     | 1020  | int3              | 500       | 2000  |
| b     | 1200  | int1              | 990       | 1090  |
| b     | 1200  | int3              | 500       | 2000  |
| c     | 1800  | int3              | 500       | 2000  |

Ответы [ 3 ]

0 голосов
/ 01 июля 2019

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Используйте выделенные / локальные инструменты, если они доступны, это хакерство:

Произошла очевидная ошибка в желаемом выводе: имя b не должно совпадать int1.

$ tail -n+1 *.csv
==> f1.csv <==
name,pos
a,1020
b,1200
c,1800

==> f2.csv <==
interval_name,start,end
int1,990,1090
int2,1100,1150
int3,500,2000

$ awk -F, -vOFS=, '
  BEGIN {
    print "name,pos,interval_name,start,end"
    PROCINFO["sorted_in"]="@ind_num_asc"
  }
  FNR==1 {next}
  NR==FNR {Int[$1] = $2 "," $3; next}
  {
    for(i in Int) {
      split(Int[i], I)
      if($2 >= I[1] && $2 <= I[2]) print $0, i, Int[i]
    }
  }
' f2.csv f1.csv

Выходы:

name,pos,interval_name,start,end
a,1020,int1,990,1090
a,1020,int3,500,2000
b,1200,int3,500,2000
c,1800,int3,500,2000

Это не особенно эффективно в любом случае;единственная используемая сортировка состоит в том, чтобы гарантировать, что массив Int анализируется в правильном порядке, который изменяется, если ваши данные выборки не указывают на фактическую схему.Мне было бы очень интересно узнать, как мое решение работает с пандами.

0 голосов
/ 01 июля 2019

Вот один в awk.Он хэширует меньшие записи файла в массивы, и для каждой из больших записей файла он перебирает хэши, поэтому он медленный:

$ awk '
NR==FNR {                             # hash f2 records
    start[NR]=$4
    end[NR]=$6
    data[NR]=substr($0,2)
    next
}
FNR<=2 {                              # mind the front matter
    print $0 data[FNR]
}
{                                     # check if in range and output
    for(i in start)
        if($4>start[i] && $4<end[i])
            print $0 data[i]
}' f2 f1

Вывод:

| name  | pos   | interval_name     | start     | end   |
|------ |------ |---------------    |-------    |------ |
| a     | 1020  | int1              | 990       | 1090  |
| a     | 1020  | int3              | 500       | 2000  |
| b     | 1200  | int3              | 500       | 2000  |
| c     | 1800  | int3              | 500       | 2000  |
0 голосов
/ 01 июля 2019

Я сомневаюсь, что bash-скрипт будет быстрее, чем Python-скрипт.Просто не импортируйте файлы в базу данных - вместо этого напишите пользовательскую функцию соединения!

Лучший способ объединения зависит от ваших входных данных.Если почти все F1.pos находятся внутри почти всех интервалов, то наивный подход будет самым быстрым.Наивный подход в bash будет выглядеть следующим образом:

#! /bin/bash
join --header -t, -j99 F1 F2 |
sed 's/^,//' |
awk -F, 'NR>1 && $2 >= $4 && $2 <= $5'
# NR>1 is only there to skip the column headers

Однако это будет очень медленно, если будет только несколько пересечений, например, когда среднее значение F1.pos только в 5 интервалах.В этом случае следующий подход будет намного быстрее.Реализуйте его на выбранном вами языке программирования - bash для этого не подходит:

  • Сортировка F1 по pos в порядке возрастания.
  • Сортировка F2 по start, а затемна end в порядке возрастания.
  • Для каждого отсортированного файла сохраняйте указатель на строку, начинающуюся с первой строки.
  • Повторяйте, пока указатель F1 не достигнет конца:
    • Для текущего F1.pos перемещать указатель F2 до F1.posF2.start.
    • Блокировать указатель F2, но продолжать читать строки до F1.posF2.end.Вывести строки чтения в формате вывода name,pos,interval_name,start,end.
    • Переместить указатель F1 на одну строку.

На самом деле только сортировка файлов может быть быстрее в bash,Вот скрипт для сортировки обоих файлов.

#! /bin/bash
sort -t, -n -k2 F1-without-headers > F1-sorted
sort -t, -n -k2,3 F2-without-headers > F2-sorted

Попробуйте использовать LC_ALL=C, -S N% и --parallel N для ускорения процесса сортировки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...