How to split awk field correctly - awk

I have a file (test.bed) that looks like this (which might not be tab-seperated):
chr1 10002 10116 id=1;frame=0;strand=+; 0 +
chr1 10116 10122 id=2;frame=0;strand=+; 0 +
chr1 10122 10128 id=3;frame=0;strand=+; 0 +
chr1 10128 10134 id=4;frame=0;strand=+; 0 +
chr1 10134 10140 id=5;frame=0;strand=+; 0 +
chr1 10140 10146 id=6;frame=0;strand=+; 0 +
chr1 10146 10182 id=7;frame=0;strand=+; 0 +
chr1 10182 10188 id=8;frame=0;strand=+; 0 +
chr1 10188 10194 id=9;frame=0;strand=+; 0 +
chr1 10194 10200 id=10;frame=0;strand=+; 0 +
I want to produce the following output (which should be tab-seperated):
chr1 10002 10116 id=1 0 +
chr1 10116 10122 id=2 0 +
chr1 10122 10128 id=3 0 +
chr1 10128 10134 id=4 0 +
chr1 10134 10140 id=5 0 +
chr1 10140 10146 id=6 0 +
chr1 10146 10182 id=7 0 +
chr1 10182 10188 id=8 0 +
chr1 10188 10194 id=9 0 +
chr1 10194 10200 id=10 0 +
I have tried with the following code:
awk 'OFS="\t" split ($0, a, ";"){print a[1],$5,$6}' test.bed
But then I get:
chr1 10002 10116 id=1 40 4+
chr1 10116 10122 id=2 40 4+
chr1 10122 10128 id=3 40 4+
chr1 10128 10134 id=4 40 4+
chr1 10134 10140 id=5 40 4+
chr1 10140 10146 id=6 40 4+
chr1 10146 10182 id=7 40 4+
chr1 10182 10188 id=8 40 4+
chr1 10188 10194 id=9 40 4+
chr1 10194 10200 id=10 40 4+
What am I doing wrong? Somehow the number '4' is added to the last two fields. I thought the number '4' somehow might have something to do with splitting in the 4th field, however, I tried producing a similar file where it was the 3rd field that was split, and still got the number '4' added to the last two fields. I am rather new to 'awk' so I guess it is an error in the syntax. Any help would be appreciated.

If you set your field separator as whitespace or semi-columns you won't have to handle the splitting yourself:
$ awk '{print $1,$2,$3,$4,$8,$9}' FS='[[:space:]]+|;' OFS='\t' file
chr1 10002 10116 id=1 0 +
chr1 10116 10122 id=2 0 +
chr1 10122 10128 id=3 0 +
chr1 10128 10134 id=4 0 +
chr1 10134 10140 id=5 0 +
chr1 10140 10146 id=6 0 +
chr1 10146 10182 id=7 0 +
chr1 10182 10188 id=8 0 +
chr1 10188 10194 id=9 0 +
chr1 10194 10200 id=10 0 +
As for what you are doing wrong in:
awk 'OFS="\t" split ($0, a, ";"){print a[1],$5,$6}'
The syntax of awk is condition{block} and setting the value of OFS and splitting is not a conditional. They are statements that should be inside the block.
However you really don't need to set the value of OFS on every line so it should be initialized only once. You can do this using the -v option, in the BEGIN block or after the script.
Valid alternatives:
$ awk -v OFS='\t' '{split($0,a,";");print a[1],$5,$6}' file
$ awk 'BEGIN{OFS="\t"}{split($0,a,";");print a[1],$5,$6}' file
$ awk '{split ($0,a,";");print a[1],$5,$6}' OFS='\t' file

Try this :
awk -F\; '{print $1,$4}' test.bed

Related

awk to calculate difference between two files and output specific text based on value

I am trying to use awk to check if each $2 in file1 falls between $2 and $3 of the matching $4 line of file2. If it does then in $5 of file2, exon if it does not intron. I think the awk below will do that, but I am struggling trying to is add a calculation that if the difference is less than or equal to 10, then $5 is splicing. I have added an example of line 1 as well.
The 6th line is an example of the splicing, because the $2 value in file1 is 2 away from the $2 value in file2. My actual data is very large with file2 always being several hundreds of thousand lines. File 1 will be variable but usually ~100 lines. The files are hardcoded in this example but will be gotten from a bash for loop. That will provide the input. Thank you :).
file1 tab-delimited with whitespace after $3 and $4
chr1 17345304 17345315 SDHB
chr1 17345516 17345524 SDHB
chr1 93306242 93306261 RPL5
chr1 93307262 93307291 RPL5
chrX 153295819 153296875 MECP2
chrX 153295810 153296830 MECP2
file2 tab-delimited
chr1 17345375 17345453 SDHB_cds_0_0_chr1_17345376_r 0 -
chr1 17349102 17349225 SDHB_cds_1_0_chr1_17349103_r 0 -
chr1 17350467 17350569 SDHB_cds_2_0_chr1_17350468_r 0 -
chr1 17354243 17354360 SDHB_cds_3_0_chr1_17354244_r 0 -
chr1 17355094 17355231 SDHB_cds_4_0_chr1_17355095_r 0 -
chr1 17359554 17359640 SDHB_cds_5_0_chr1_17359555_r 0 -
chr1 17371255 17371383 SDHB_cds_6_0_chr1_17371256_r 0 -
chr1 17380442 17380514 SDHB_cds_7_0_chr1_17380443_r 0 -
chr1 93297671 93297674 RPL5_cds_0_0_chr1_93297672_f 0 +
chr1 93298945 93299015 RPL5_cds_1_0_chr1_93298946_f 0 +
chr1 93299101 93299217 RPL5_cds_2_0_chr1_93299102_f 0 +
chr1 93300335 93300470 RPL5_cds_3_0_chr1_93300336_f 0 +
chr1 93301746 93301949 RPL5_cds_4_0_chr1_93301747_f 0 +
chr1 93303012 93303190 RPL5_cds_5_0_chr1_93303013_f 0 +
chr1 93306107 93306196 RPL5_cds_6_0_chr1_93306108_f 0 +
chr1 93307322 93307422 RPL5_cds_7_0_chr1_93307323_f 0 +
chrX 153295817 153296901 MECP2_cds_0_0_chrX_153295818_r 0 -
chrX 153297657 153298008 MECP2_cds_1_0_chrX_153297658_r 0 -
chrX 153357641 153357667 MECP2_cds_2_0_chrX_153357642_r 0 -
desired output tab-delimited
chr1 17345304 17345315 SDHB intron
chr1 17345516 17345524 SDHB intron
chr1 93306242 93306261 RPL5 intron
chr1 93307262 93307291 RPL5 intron
chrX 153295819 153296875 MECP2 exon
chrX 153295810 153296800 MECP2 splicing
awk
awk '
FNR==NR{
a[$4];
min[$4]=$2;
max[$4]=$3;
next
}
{
split($4,array,"_");
print $0,(array[1] in a) && ($2>=min[array[1]] &&
$2<=max[array[1]])?"exon":"intron"
}' file1 OFS="\t" file2 > output
example of line 1
a[$4] = SDHB
min[$4] = 17345304
max[$4] = 17345315
array[1] = SDHB, 17345304 >= 17345375 && array[1] = SDHB, 17345315 <= 17345453 ---- intron

Awk OR conditional not working

Input: A tab-separated input file with 15 columns where column 15 is an integer.
Output: The number of lines that satisfy the conditional.
My code:
$ closest-features --closest --no-overlaps --delim '\t' --dist --ec megatrans_enhancers.sorted.bed ../../data/alu_repeats.sorted.bed | awk -v OFS='\t' '{if ($15 <= 1000 || $15 >= -1000) print $0}' | wc -l
1188
The || conditional in this case is failing to work (the total number of lines in the file are 1188 and I know for certain at least some lines do not satisfy the condition), because if I remove the OR conditional then suddenly it works:
$ closest-features --closest --no-overlaps --delim '\t' --dist --ec megatrans_enhancers.sorted.bed ../../data/alu_repeats.sorted.bed | awk -v OFS='\t' '{if ($15 <= 1000) print $0}' | wc -l
926
Not sure what i'm doing wrong. Any advice?
Example Input to Awk command:
chr1 378268 378486 chr1-798_Enhancer 17.2 + chr1 375923 376219 AluY|SINE|Alu-HOMER529 0 + E:375923 0.044 -2050
chr1 1079471 1079689 chr1-929_Enhancer 14.6 - chr1 1071271 1071563 AluSx1|SINE|Alu-HOMER1669 0 - E:1071271 0.13 -7909
chr1 1080259 1080477 chr1-830_Enhancer 16.7 - chr1 1071271 1071563 AluSx1|SINE|Alu-HOMER1669 0 - E:1071271 0.13 -8697
chr1 6611744 6611962 chr1-241_Enhancer 46.6 + chr1 6611431 6611723 AluSc|SINE|Alu-HOMER10257 0 + E:6611431 0.089 -22
chr1 6959639 6959857 chr1-58_Enhancer 100.1 - chr1 6966612 6966911 AluSx|SINE|Alu-HOMER11041 0 - E:6966612 0.137 6756
chr1 6960593 6960811 chr1-202_Enhancer 51.6 - chr1 6966612 6966911 AluSx|SINE|Alu-HOMER11041 0 - E:6966612 0.137 5802
chr1 7447888 7448106 chr1-2_Enhancer 181.9 - chr1 7449489 7449799 AluSz|SINE|Alu-HOMER11879 0 + E:7449489 0.119 1384
chr1 10752461 10752679 chr1-131_Enhancer 65.4 - chr1 10752754 10753065 AluSq2|SINE|Alu-HOMER19455 0 + E:10752754 0.106 76
chr1 12485694 12485912 chr1-353_Enhancer 36.7 + chr1 12487328 12487634 AluSx3|SINE|Alu-HOMER23581 0 + E:12487328 0.085 1417
chr1 12486469 12486687 chr1-141_Enhancer 63.6 + chr1 12487328 12487634 AluSx3|SINE|Alu-HOMER23581 0 + E:12487328 0.085 642
Try to put && condition because a digit should be greater than -1000 and lesser than 1000.
Your_command | awk '$15<=1000 && $15>=-1000{count++} END{print count}'
Add -F"\t" in above awk in case your Input to it is coming TAB delimited too. Also there is no need to use wc -l after awk. I have written logic for that so give the count of lines which are satisfying the condition by creating a variable named count and printing it at very last of Input_file.
Also for your provided samples output is coming as 3 which I believe is correct one.

awk to find and output differences in files

I am trying to find the differences between file1.txt and file2.txt and output the differences. I tried diff and sed and the output does not return any differences. I also tried awk and matching on $2, but I think the syntax is wrong as a file gets created but it is 0kb. The actual data I am using is quite large but I know there should be 18 differences. Thank you :).
awk 'NR==FNR{a[$2]++;next} !($2 in a){print $2}' file1.txt file2.txt > diff.txt
file1.txt
chr1 955542 955763
chr1 957570 957852
chr1 976034 976270
file2.txt
chr1 955542 955763 + AGRN:exon.1
chr1 957570 957852 + AGRN:exon.2
chr1 976034 976270 + AGRN:exon.2;AGRN:exon.3;AGRN:exon.4
chr1 976542 976787 + AGRN:exon.3;AGRN:exon.5
chr1 976847 977092 + AGRN:exon.6
Desired output
chr1 976542 976787 + AGRN:exon.3;AGRN:exon.5
chr1 976847 977092 + AGRN:exon.6
Diff result (since these are the two records that are not in both files)
1,52058c1,52040
< chr1 955542 955763
< chr1 957570 957852
< chr1 976034 976270
I'm curious while diff isn't working like you want, but your awk logic isn't correct:
You're checking the second field's (delimited by spaces) value only. In your example the second field is all identical so nothing is being printed out. Using the whole line instead works as expected:
Using your example text where all is different:
$ cat file1.txt
chr1 955542 955763
chr1 957570 957852
chr1 976034 976270
$ cat file2.txt
chr1 955542 955763 + AGRN:exon.1
chr1 957570 957852 + AGRN:exon.2
chr1 976034 976270 + AGRN:exon.2;AGRN:exon.3;AGRN:exon.4
$ awk 'NR==FNR{a[$0]++;next} !($0 in a){print $0}' file1.txt file2.txt > diff.txt
$ cat diff.txt
chr1 955542 955763 + AGRN:exon.1
chr1 957570 957852 + AGRN:exon.2
chr1 976034 976270 + AGRN:exon.2;AGRN:exon.3;AGRN:exon.4
Here's with the second line identical just to show it working in a more obvious way.
$ cat file1.txt
chr1 955542 955763
chr1 957570 957852
chr1 976034 976270
$ cat file2.txt
chr1 955542 955763 + AGRN:exon.1
chr1 957570 957852
chr1 976034 976270 + AGRN:exon.2;AGRN:exon.3;AGRN:exon.4
$ awk 'NR==FNR{a[$0]++;next} !($0 in a){print $0}' file1.txt file2.txt > diff.txt
$ cat diff.txt
chr1 955542 955763 + AGRN:exon.1
chr1 976034 976270 + AGRN:exon.2;AGRN:exon.3;AGRN:exon.4
EDIT
Based on a comment stating:
"There should be 18 differences out of 52,000 lines. File1.txt is 52,058 entries and file2.txt has 52,040 entries in it. I am trying to find out what the 18 are"
Given you said file1 has more lines, you need to process file2 first. The first file read is populating the array and then the second is checking for lines existing in that array. You need to process the smaller file first so that the additional lines you're interested in aren't in the array. It'd be the same logic above, just with the file order switched, e.g.:
$ cat file1.txt
chr1 955542 955763
chr1 957570 957852
chr1 976034 976270
New Line!
Not in file2!
$ cat file2.txt
chr1 955542 955763 + AGRN:exon.1
chr1 957570 957852
chr1 976034 976270 + AGRN:exon.2;AGRN:exon.3;AGRN:exon.4
$ awk 'NR==FNR{a[$0]++;next} !($0 in a){print $0}' file2.txt file1.txt > diff.txt
$ cat diff.txt
chr1 955542 955763
chr1 976034 976270
New Line!
Not in file2!
$ awk 'NR==FNR{a[$0]++;next} !($0 in a){print $0}' file1.txt file2.txt > diff.txt
$ cat diff.txt
chr1 955542 955763 + AGRN:exon.1
chr1 976034 976270 + AGRN:exon.2;AGRN:exon.3;AGRN:exon.4
Note that reading file1 first doesn't emit the additional lines.
If you don't care about the additional text on the lines, just the text in the second field, then you could use $2 as you originally did.
$ awk 'NR==FNR{a[$2];next} !($2 in a)' file1 file2
chr1 976542 976787 + AGRN:exon.3;AGRN:exon.5
chr1 976847 977092 + AGRN:exon.6

LINUX AWK command for a big file

I have encountered a problem that exceeds my basic unix knowledge and would really appreciate some help. I have a large file in the following format:
chr1 10495 10499 211
chr1 10496 10500 1
chr1 10587 10591 93
chr1 10588 10592 1
chr1 10639 10643 4
chr1 10668 10672 11
chr1 10697 10701 13
chr1 10726 10730 8
chr1 10755 10759 7
chr1 10784 10788 5
chr2 10856 10860 4
chr3 10932 10936 6
chr3 10933 10937 2
chr5 11056 11060 4
chr6 11155 11159 9
If the values in column one match and one number difference in column two, I want to sum the values in column 4 of both lines and replace the value of column 3 in line 1 with the value of column 3 in line 2 , else just the the values in the unique line without modifying any column.
So the output I am hoping for would look like this:
chr1 10495 10500 212
chr1 10587 10592 94
chr1 10639 10643 4
chr1 10668 10672 11
chr1 10697 10701 13
chr1 10726 10730 8
chr1 10755 10759 7
chr1 10784 10788 5
chr2 10856 10860 4
chr3 10932 10937 8
chr5 11056 11060 4
chr6 11155 11159 9
$ cat tst.awk
BEGIN { OFS="\t" }
NR>1 {
if ( ($1==p[1]) && ($2==(p[2]+1)) ) {
print p[1], p[2], $3, p[4]+$4
delete p[0]
next
}
else if (0 in p) {
print p[0]
}
}
{ split($0,p); p[0]=$0 }
END { if (0 in p) print p[0] }
$
$ awk -f tst.awk file
chr1 10495 10500 212
chr1 10587 10592 94
chr1 10639 10643 4
chr1 10668 10672 11
chr1 10697 10701 13
chr1 10726 10730 8
chr1 10755 10759 7
chr1 10784 10788 5
chr2 10856 10860 4
chr3 10932 10937 8
chr5 11056 11060 4
chr6 11155 11159 9
Haven't checked closely, but I think you want:
awk '{split(p,a)}
$1==a[1] && a[2]==$2-1{print a[1], a[2], $3, $4 + a[4]; p=""; next}
p {print p} {p=$0}
END {print}' OFS=\\t input
At any given step (except the first), p holds the value from the previous line. The 2nd line of the script checks if the first field in the current line matches the first field of the last line and that the 2nd field is one greater than the 2nd field of the last line. In that condition, it prints the first two fields from the previous line, the third from the current line, and the sum of the 4th fields and moves on to the next line. If they don't match, it prints the previous line. At the end, it just prints the line.
This script, I'm using to merge intervals in transcriptome data
awk '
NR==1{
n= split($0, first);
c=1;
for(i=1; i<=n; i++) d[c, i] = first[i];
}
NR>1{
n= split($0, actual);
#if(actual[1] != d[c, 1] || actual[2]>d[c, 3]){ #for interval fusion
if(actual[1] != d[c, 1] || actual[2]>d[c,2]+1){ #OP requirement
c++;
for(i=1; i<=n; i++) d[c, i] = actual[i];
}else{
if(actual[3] > d[c,3]) d[c,3] = actual[3];
d[c,4] = d[c,4] + actual[4];
}
}
END{
for(i=1; i<=c; i++){
print d[i, 1], d[i, 2], d[i, 3], d[i, 4]
}
}' file
you get:
chr1 10495 10500 212
chr1 10587 10592 94
chr1 10639 10643 4
chr1 10668 10672 11
chr1 10697 10701 13
chr1 10726 10730 8
chr1 10755 10759 7
chr1 10784 10788 5
chr2 10856 10860 4
chr3 10932 10937 8
chr5 11056 11060 4
chr6 11155 11159 9

Awk script for my data

I have a file which has these columns:
chr1 1397031 1445511 360 chr1 1436533 1436893
chr1 3558988 3639716 9837 chr1 3565359 3575196
chr1 9634389 9711556 1958 chr1 9635273 9637231
chr1 10657207 10657742 535 chr1 10629864 10676549
chr1 12590100 12594553 4453 chr1 12550526 12600407
chr1 14599424 14601321 1897 chr1 14590538 14619056
chr1 15352815 15419459 7429 chr1 15363278 15370707
The fourth column represents the overlap between the 2nd, 3rd, 6th and the 7th columns.
So,the smaller one between the 7th column and the 3rd is the end position of the overlapping region. And the bigger one between the 2nd column and the 6th is the start position.
Can anyone help with an awk script ?
well your question is not that clear.. you described your data, but didn't mention what do you want to get..
I guess you want to list the start/end of your "overlapping" right?
awk '{s=$2>=$6?$2:$6;e=$3<=$7?$3:$7;print $1,s,e,$4}' file
the output would be:
chr1 1436533 1436893 360
chr1 3565359 3575196 9837
chr1 9635273 9637231 1958
chr1 10657207 10657742 535
chr1 12590100 12594553 4453
chr1 14599424 14601321 1897
chr1 15363278 15370707 7429
columns:
col1 is the text
col2 is the overlapping start
col3 is the overlapping end
col4 is the overlap
a magic answer to magic question...:)