]> git.donarmstrong.com Git - bin.git/blob - transpose_table
1232fe2fb49493f8b828b8418d11e5bbd19bfdd2
[bin.git] / transpose_table
1 #! /usr/bin/perl
2 # transpose_table transposes tables, and is released
3 # under the terms of the GPL version 2, or any later version, at your
4 # option. See the file README and COPYING for more information.
5 # Copyright 2011 by Don Armstrong <don@donarmstrong.com>.
6 # $Id: perl_script 1825 2011-01-02 01:53:43Z don $
7
8
9 use warnings;
10 use strict;
11
12 use Getopt::Long;
13 use Pod::Usage;
14
15 =head1 NAME
16
17 transpose_table - Transposes a table
18
19 =head1 SYNOPSIS
20
21  [options]
22
23  Options:
24   --tsv, -t tab separated value mode (Default)
25   --ssv, -s space separated value mode
26   --csv, -c comma separated value mode
27   --bigfile,-b Big file; use disk
28   --debug, -d debugging level (Default 0)
29   --help, -h display this help
30   --man, -m display manual
31
32 =head1 OPTIONS
33
34 =over
35
36 =item B<--debug, -d>
37
38 Debug verbosity. (Default 0)
39
40 =item B<--help, -h>
41
42 Display brief usage information.
43
44 =item B<--man, -m>
45
46 Display this manual.
47
48 =back
49
50 =head1 EXAMPLES
51
52
53 =cut
54
55
56 use vars qw($DEBUG);
57
58 use Text::CSV;
59
60 use File::Temp qw(tempdir);
61
62 use MLDBM qw(DB_File Storable);
63 use Fcntl;
64
65 use List::Util qw(max);
66
67 my %options = (debug           => 0,
68                help            => 0,
69                man             => 0,
70                bigfile         => 0,
71                );
72
73 GetOptions(\%options,
74            'tsv|t',
75            'ssv|s',
76            'csv|c',
77            'bigfile',
78            'debug|d+','help|h|?','man|m');
79
80 pod2usage() if $options{help};
81 pod2usage({verbose=>2}) if $options{man};
82
83 $DEBUG = $options{debug};
84
85 my @USAGE_ERRORS;
86 if (0 == grep {exists $options{$_}} qw(tsv ssv csv)) {
87     $options{tsv} = 1
88 }
89 if (1 < grep {exists $options{$_}} qw(tsv ssv csv)) {
90      push @USAGE_ERRORS,"You can only pass one of --tsv, --ssv, or --csv";
91 }
92
93 if (not @ARGV) {
94     # we'll use this as a special indicator to read stdin
95     push @ARGV,undef;
96 }
97 if (@ARGV != 1) {
98     push @USAGE_ERRORS,"Exactly one file must be specified";
99 }
100
101 pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
102
103 my $sep_char = "\t";
104 if ($options{csv}) {
105     $sep_char = ',';
106 }
107 elsif ($options{ssv}) {
108     $sep_char = ' ';
109 }
110
111 my $csv = Text::CSV->new({sep_char=>$sep_char});
112
113 my %rows;
114 my $n_rows = 0;
115
116 if ($options{bigfile}) {
117     my $temp_dir = tempdir(CLEANUP=>1);
118     tie %rows,"MLDBM","$temp_dir/bigfile", O_CREAT|O_RDWR, 0600 or
119         die "Unable to tie '$temp_dir/bigfile': $!";
120 }
121 my $cols = 0;
122 my $rows = 0;
123 for my $file (@ARGV) {
124     my $fh;
125     if (not defined $file) {
126         $fh = \*STDIN;
127         $file = "STDIN";
128     }
129     else {
130         $fh = IO::File->new($file,'r') or
131             die "Unable to open $file for reading: $!";
132     }
133     while (<$fh>) {
134         chomp;
135         # parse the line
136         die "Unable to parse line $. of $file" unless $csv->parse($_);
137         my @row = $csv->fields();
138         $cols = max(scalar @row,$cols);
139         for my $i (0..$#row) {
140             $rows{$rows.'-'.$i} = $row[$i];
141         }
142         $rows++;
143         print STDERR "\rInput $rows rows";# if not ($rows-1) % 50;
144     }
145     print STDERR "\n";
146     $fh = \*STDOUT;
147     for my $i (0..($cols-1)) {
148         my @row;
149         for my $j (0..($rows-1)) {
150             push @row,$rows{$j.'-'.$i};
151         }
152         $csv->print($fh,\@row);
153         print "\n";
154         print STDERR "\rOutput ".($i+1)."/$cols rows";
155     }
156     print STDERR "\n";
157 }
158
159 __END__