2016-03-04 3 views
0

Я думаю, если есть способ разделить столбец, сопоставляя заголовок?Как разбить столбец, сопоставляя заголовок?

Данные выглядит следующим образом

  ID_1 ID_2 ID_3 ID_6 ID_15 
value1 0  2  4  7  6 
value2 0  4  4  3  8 
value3 2  2  3  7  8 

Я хотел бы получить столбцы только на ID_3 & ID_15

ID_3 ID_15 
4  6 
4  8 
3  8 

AWK может просто отделить его, если я знаю, что порядок столбца Однако , У меня очень огромный стол и есть только список идентификаторов в руках.
Могу ли я использовать awk или есть более простой способ в Linux?

+0

См., Например, http://unix.stackexchange.com/questions/25138/how-to-print-certain-columns-by-name –

ответ

1

Формат ввода не определена, но есть несколько простых способов, awk, perl и sqlite.

(FNR==1) { 
    nocol=split(col,ocols,/,/) # cols contains named columns 
    ncols=split("vals " $0,cols) # header line 
    for (nn=1; nn<=ncols; nn++) colmap[cols[nn]]=nn # map names 

    OFS="\t"      # to align output 
    for (nn=1; nn<=nocol; nn++) printf("%s%s",ocols[nn],OFS) 
    printf("\n")     # output header line 
} 
(FNR>1) { # read data 
    for (nn=1; nn<=nocol; nn++) { 
     if (nn>1) printf(OFS)  # pad 
     if (ocols[nn] in colmap) { printf("%s",$(colmap[ocols[nn]])) } 
     else      { printf "--" } # named column not in data 
    } 
    printf("\n") # wrap line 
} 

$ nawk -f mycols.awk -v col=ID_3,ID_15 data 
ID_3 ID_15 
4  6 
4  8 
3  8 

Perl, просто вариация на выше с некоторой PERL идиомы спутать/Развлекать:

use strict; 
use warnings; 

our @ocols=split(/,/,$ENV{cols}); # cols contains named columns 
our $nocol=scalar(@ocols); 
our ($nn,%colmap); 
$,="\t";       # OFS equiv 

# while (<>) {...} implicit with perl -an 
if ($. == 1) { # FNR equiv 
    %colmap = map { $F[$_] => $_+1 } 0..$#F ; # create name map hash 
    $colmap{vals}=0;       # name anon 1st col 
    print @ocols,"\n";      # output header 
} else { 
    for ($nn = 0; $nn < $nocol; $nn++) { 
     print "\t" if ($nn>0); 
     if (exists($colmap{$ocols[$nn]})) { printf("%s",$F[$colmap{$ocols[$nn]}]) } 
     else        { printf("--") } # named column not in data 
    } 
    printf("\n") 
} 

$ cols="ID_3,ID_15" perl -an mycols.pl < data 

Это использует переменную среды, чтобы пропустить усилие разбора командной строки. Ему нужны параметры perl , которые устанавливают разделение поля и входной цикл чтения (как и awk).


И с sqlite (я использовал v3.11, v3.8 или более поздней версии требуется для полезной .import я считаю).Это использует временную базу данных в памяти (имя файла, если оно слишком велико для памяти, или для постоянной копии проанализированных данных), и автоматически создает таблицу на основе первой строки. Преимущества здесь в том, что вам может вообще не понадобиться никаких сценариев, и вы можете выполнять несколько запросов в своих данных только с одним служебным изложением анализа.

Вы можете пропустить следующий шаг, если у вас есть один жесткого язычок ограничивающих столбцы, в этом случае заменить .mode csv с .mode tab в SQLITE примера ниже. В противном случае, чтобы преобразовать данные в подходящий CSV-иш формат:

nawk -v OFS="," '(FNR==1){$0="vals " $0} {$1=$1;print} <data> data.csv 

Это добавляет фиктивный первый столбец «Vals» в первую строку, а затем выводит каждую строку как разделенные запятые, он делает это казалось бы, бессмысленное назначение $1, но это приводит к перерасчету $0, заменяющему FS (пробел/табуляцию) OFS (запятая).

$ sqlite3 
sqlite> .mode csv 
sqlite> .import data.csv mytable 
sqlite> .schema mytable 
CREATE TABLE mytable(
    "vals" TEXT, 
    "ID_1" TEXT, 
    "ID_2" TEXT, 
    "ID_3" TEXT, 
    "ID_6" TEXT, 
    "ID_15" TEXT 
); 
sqlite> select ID_3,ID_15 from mytable; 
ID_3,ID_15 
4,6 
4,8 
3,8 
sqlite> .mode column 
sqlite> select ID_3,ID_15 from mytable; 
ID_3  ID_15  
---------- ---------- 
4   6   
4   8   
3   8   

Использование .once или .output для отправки вывода в файл (sqlite docs). Используйте .headers on или .headers off при необходимости. sqlite вполне счастлив создать неназванный столбец, поэтому вам не нужно добавлять имя в первый столбец строки заголовка, но вам нужно убедиться, что количество столбцов одинаково для всех строк ввода и форматов ,

Если в течение .import вы получили «ожидаемые столбцы X, но обнаружили ошибки Y», вам потребуется немного очистить формат данных.

+0

Большое вам спасибо. Я попробовал первый скрипт awk, и он работает. Тем не менее, кажется, что я не мог определить первый столбец, даже я дал правильный заголовок ..... – user3631848

+0

Извините, в третьей строке скрипта awk было пустое место, где анонимному первому столбцу присваивается имя, исправлено ! –

1
$ cat c.awk 
NR == 1 { 
    for (i=1; i<=NF; ++i) { 
     if ($i == "ID_3") col_3 = (i + 1) 
     if ($i == "ID_15") col_15 = (i + 1) 
    } 
    print "ID_3", "ID_15" 
} 

NR > 1 { print $col_3, $col_15 } 


$ awk -f c.awk c.txt 
ID_3 ID_15 
4 6 
4 8 
3 8 
+0

Спасибо. Что делать, если мне нужно собрать сотни совпадающих заголовков? По крайней мере, я получил некоторые идеи из вашего предложения. Спасибо. – user3631848

1

Вы могли бы пойти на что-то вроде этого:

BEGIN { 
    keys["ID_3"] 
    keys["ID_15"] 
} 

NR == 1 { 
    for (i = 1; i <= NF; ++i) 
     if ($i in keys) cols[++n] = i 
} 

{ 
    for (i = 1; i <= n; ++i) 
     printf "%s%s", $(cols[i]+(NR>1)), (i < n ? OFS : ORS) 
} 

Сохраните скрипт в файл и запустить его как awk -f script.awk file.

В качестве альтернативы, как «один-лайнер»:

awk 'BEGIN { keys["ID_3"]; keys["ID_15"] } 
NR == 1 { for (i = 1; i <= NF; ++i) if ($i in keys) cols[++n] = i } 
{ for (i = 1; i <= n; ++i) printf "%s%s", $(cols[i]+(NR>1)), (i < n ? OFS : ORS) }' file 

Перед тем как файл обрабатывается, ключи устанавливаются в keys массива, соответствующие заголовки столбцов, представляющих интерес.

В первой строке запишите все номера столбцов, содержащие одну из клавиш в массиве cols.

Пройдите через каждый из столбцов и распечатайте их, а затем разделитель выходного поля OFS или разделитель выходной записи ORS, в зависимости от того, является ли он последним. $(cols[i]+(NR>1)) обрабатывает тот факт, что строки после первого имеют дополнительное поле в начале, потому что NR>1 будет истинным (1) для этих строк и false (0) для первой строки.

+1

Да, это немного плотно, по общему признанию. Я отредактировал его, чтобы добавить его в форме скрипта. –

+1

Да, я был ленив ... Я отредактировал его, чтобы использовать правильные –

+0

Спасибо. Это выглядит просто и понятно. Тем не менее, я пытаюсь получить сотни совпадающих заголовков. Создать правило сопоставления заголовков в скрипте кажется нецелесообразным. Однако я понял это. Спасибо. – user3631848

0

Try ниже сценария:

#!/bin/sh 

file="$1"; shift 

awk -v cols="$*" ' 
BEGIN{ 
split(cols,C) 
OFS=FS="\t" 
getline 
split($0,H) 
for(c in C){ 
    for(h in H){ 
     if(C[c]==H[h])F[i++]=h 
    } 
} 
} 
{ l="";for(f in F){l=l $F[f] OFS}print l } 

' "$file" 

В командной строке введите:

[[email protected] ~]$ test.sh filename ID_3 ID_5 
+1

Вы не можете использовать 'for (f in F)' для итерации по порядку, даже если индексы являются целыми числами, это будет регулярно выводить столбцы в неправильном порядке в зависимости от реализации awk. –

+0

Кроме того, никогда не называйте переменную 'l', поскольку она выглядит слишком сильно, как' 1', и вы не используете 'getline' - see.http: //awk.info/? Tip/getline. –

Смежные вопросы