8

I have a file which has three columns. Column 3 contains names of genes and it looks like this:

Rv0729,Rv0993,Rv1408  
Rv0162c,Rv0761c,Rv1862,Rv3086  
Rv2790c

How can I print the number of genes in each row?

3
  • A fourth column? What should happen if that column is already occupied (second row in your example), or if the other columns are empty (last row)?
    – Kusalananda
    Mar 8, 2017 at 10:04
  • @Kusalananda Removed that criterion from my query :)
    – Saisha
    Mar 8, 2017 at 10:06
  • On a quick look, all the answers count the comma-separated fields or strings matching the Rv* pattern anywhere on the line not just in a particular column. So I'll just note that if you actually have other data in the file, not presented in the question here, you may need to modify the solutions accordingly. (Or clarify the question.)
    – ilkkachu
    Mar 8, 2017 at 14:24

4 Answers 4

10

You simply want to add a column with the count of columns in it. This may be done using awk:

$ awk -F ',' '{ printf("%d,%s\n", NF, $0) }' data.in
3,Rv0729,Rv0993,Rv1408
4,Rv0162c,Rv0761c,Rv1862,Rv3086
1,Rv2790c

NF is an awk variable containing the number of fields (columns) in the current record (row). We print this number followed by a comma and the rest of the row, for each row.

An alternative (same result, but may look a bit cleaner):

$ awk -F ',' 'BEGIN { OFS=FS } { print NF, $0 }' data.in

FS is the field separator which awk uses to split each record into fields, and we set that to a comma with -F ',' on the command line (as in the first solution). OFS is the output field separator, and we set that to be the same as FS before reading the first line of input.

0
5

If you wanted to count the number of occurrences of the Rv[0-9]{4}c? pattern as opposed to the number of comma-delimited fields as the subject of your question suggests, you could do:

 awk '{print gsub(/Rv[0-9]{4}c?/, "&"), $0}'
4

A Perl approach:

$ perl -F, -pae 's/^/$#F+1 . ","/e' file
3,Rv0729,Rv0993,Rv1408  
4,Rv0162c,Rv0761c,Rv1862,Rv3086  
1,Rv2790c

The -a makes perl behave like awk and split each input line on the string given by -F and save the resulting fields into the array @F. Therefore, $#F will be the highest array index in @F and, since arrays start counting at 0, $#F+1 will be the total number of elements in the array. The -p means "print every input line after aplpying the script given by -e. The s/// is the substitution operator and here were are replacing the beginning of the line (^) with the number of fields + 1 and a comma ($#F+1 . ",").

1

Your question states that column 3 contains names of genes. I assume your actual input is as follows:

column1 column2 Rv0729,Rv0993,Rv1408  
column1 column2 Rv0162c,Rv0761c,Rv1862,Rv3086  
column1 column2 Rv2790c

Each gene name in column3 contains leading Rv substring. Thus we can count them in python as so:

$ python -c  "import sys;print map(lambda x: x.split()[2].count('Rv'),sys.stdin.readlines())"  < input.txt               
[3, 4, 1]

The resulting list shows count of genes in each line, in their respective order. If we want to make it more verbose and include the possibility that genes might not contain "Rv" string (but keep the assumption that column3 is comma-separated value string), we can also do the following:

#!/usr/bin/env python
import sys
with open(sys.argv[1]) as fd:
    for index,line in enumerate(fd):
        columns = line.strip().split()
        num_genes=len(columns[2].split(","))
        print("Line "+str(index)+" contains "+str(num_genes))

Test run:

$ ./count_genes.py input.txt                                                                                             
Line 0 contains 3
Line 1 contains 4
Line 2 contains 1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .