NAME
Text::CSV::Pivot - Transform CSV file into Pivot Table format.
VERSION
Version 0.09
DESCRIPTION
Recently I was asked to prepare pivot table using csv file at work. Having done that using quick and dirty perl script, I decided to clean up and make it generic so that others can also benefit.
Below is sample data, I used for prototype as source csv file.
+----------------+-----------+--------+--------+
| Student | Subject | Result | Year |
+----------------+-----------+--------+--------+
| Smith, John | Music | 7.0 | Year 1 |
| Smith, John | Maths | 4.0 | Year 1 |
| Smith, John | History | 9.0 | Year 1 |
| Smith, John | Language | 7.0 | Year 1 |
| Smith, John | Geography | 9.0 | Year 1 |
| Gabriel, Peter | Music | 2.0 | Year 1 |
| Gabriel, Peter | Maths | 10.0 | Year 1 |
| Gabriel, Peter | History | 7.0 | Year 1 |
| Gabriel, Peter | Language | 4.0 | Year 1 |
| Gabriel, Peter | Geography | 10.0 | Year 1 |
+----------------+-----------+--------+--------+
I aim to get something like this below.
+----------------+--------+-----------+---------+----------+-------+-------+
| Student | Year | Geography | History | Language | Maths | Music |
+----------------+--------+-----------+---------+----------+-------+-------+
| Gabriel, Peter | Year 1 | 10.0 | 7.0 | 4.0 | 10.0 | 2.0 |
| Smith, John | Year 1 | 9.0 | 9.0 | 7.0 | 4.0 | 7.0 |
+----------------+--------+-----------+---------+----------+-------+-------+
With the help of Text::CSV::Pivot, I came up with the following solution.
use strict; use warnings;
use Text::CSV::Pivot;
Text::CSV::Pivot->new({ input_file => 'sample.csv',
col_key_idx => 0,
col_name_idx => 1,
col_value_idx => 2 })->transform;
After executing the above code, I got the expected result in sample.pivot.csv
.
SYNOPSIS
Let's assume we have the following source csv file (sample.csv):
+----------------+-----------+-----------------+
| Student | Subject | Result | Year |
+----------------+-----------+--------+--------+
| Smith, John | Music | 7.0 | Year 1 |
| Smith, John | Maths | 4.0 | Year 1 |
| Smith, John | History | 9.0 | Year 1 |
| Smith, John | Geography | 9.0 | Year 1 |
| Gabriel, Peter | Music | 2.0 | Year 1 |
| Gabriel, Peter | Maths | 10.0 | Year 1 |
| Gabriel, Peter | History | 7.0 | Year 1 |
| Gabriel, Peter | Language | 4.0 | Year 1 |
+----------------+-----------+--------+--------+
If you notice, the student "Smith, John"
do not have any score for the subject "Language"
and the student "Gabriel, Peter"
missing score for "Geography"
.
use strict; use warnings;
use Text::CSV::Pivot;
Text::CSV::Pivot->new({ input_file => 'sample.csv',
col_key_idx => 0,
col_name_idx => 1,
col_value_idx => 2 })->transform;
The above code would then create the result in sample.pivot.csv
as below:
+----------------+--------+-----------+---------+----------+-------+-------+
| Student | Year | Geography | History | Language | Maths | Music |
+----------------+--------+-----------+---------+----------+-------+-------+
| Gabriel, Peter | Year 1 | 10.0 | 7.0 | | 10.0 | 2.0 |
| Smith, John | Year 1 | | 9.0 | 7.0 | 4.0 | 7.0 |
+----------------+--------+-----------+---------+----------+-------+-------+
In case, we would want to skip "Year"
column then the following code:
use strict; use warnings;
use Text::CSV::Pivot;
Text::CSV::Pivot->new({ input_file => 'sample.csv',
col_key_idx => 0,
col_name_idx => 1,
col_value_idx => 2,
col_skip_idx => [3] })->transform;
You should get the result in sample.pivot.csv
as below:
+----------------+-----------+---------+----------+-------+-------+
| Student | Geography | History | Language | Maths | Music |
+----------------+-----------+---------+----------+-------+-------+
| Gabriel, Peter | 10.0 | 7.0 | | 10.0 | 2.0 |
| Smith, John | | 9.0 | 7.0 | 4.0 | 7.0 |
+----------------+-----------+---------+----------+-------+-------+
CLI
With the Text::CSV::Pivot v0.06
or above, there is a CLI tool supplied with the distribution called csv-pivot
.
Usage: csv-pivot [OPTIONS]...
OPTIONS:
-i, --i, -input-file, --input-file=s input file (required)
-o, --o, -output-file, --output-file=s output file (optional)
-k, --k, -col-key-idx, --col-key-idx=i key column index (required)
-n, --n, -col-name-idx, --col-name-idx=i name column index (required)
-v, --v, -col-value-idx, --col-value-idx=i value column index (required)
-s, --s, -col-skip-idx, --col-skip-idx=s comma separated skip column index (optional)
-h, --help print this message
If you want to do something like below:
use strict; use warnings;
use Text::CSV::Pivot;
Text::CSV::Pivot->new({ input_file => 'sample.csv',
col_key_idx => 0,
col_name_idx => 1,
col_value_idx => 2,
col_skip_idx => [3] })->transform;
Then this can be achieved using CLI csv-pivot
like below:
$ cvs-pivot -i sample.csv -k 0 -n 1 -v 2 -s 3
CONSTRUCTOR
The following table explains the parameters for the constructor. However you can also pass any valid parameters for Text::CSV
.
+---------------+----------+---------------------------------------------------+
| Name | Required | Description |
+---------------+----------+---------------------------------------------------+
| input_file | Yes | Path to the source csv file. |
| output_file | No | Path to the output csv file. |
| col_key_idx | Yes | Column index that uniquely identify each row. |
| col_name_idx | Yes | Column index that would provide new column name. |
| col_value_idx | Yes | Column index that would provide new column value. |
| col_skip_idx | No | Column index to ignore in the output csv. |
+---------------+----------+---------------------------------------------------+
Please note output_file
is optional. If missing, it would create one for you. For example if you provide input_file
as "sample.csv" then it would create output file as "sample.pivot.csv".
Column index starts with 0, left to right. So in the example below, the col_key_idx
would be 0. Similarly col_name_idx
and col_value_idx
would be 1 and 2 resp. In case, we would want to skip the column "Year"
in the output file, then col_skip_idx
would be [3]. All index related parameters except col_skip_idx
would expect number 0 or more. The col_skip_idx
would expected an ArrayRef
of column index.
+----------------+-----------+--------+--------+
| Student | Subject | Result | Year |
+----------------+-----------+--------+--------+
| Smith, John | Music | 7.0 | Year 1 |
| Smith, John | Maths | 4.0 | Year 1 |
| Smith, John | History | 9.0 | Year 1 |
| Smith, John | Language | 7.0 | Year 1 |
| Smith, John | Geography | 9.0 | Year 1 |
| Gabriel, Peter | Music | 2.0 | Year 1 |
| Gabriel, Peter | Maths | 10.0 | Year 1 |
| Gabriel, Peter | History | 7.0 | Year 1 |
| Gabriel, Peter | Language | 4.0 | Year 1 |
| Gabriel, Peter | Geography | 10.0 | Year 1 |
+----------------+-----------+--------+--------+
Let's assume, we want column "Student"
to be our key column, the "Subject"
column to provide us the new column name and "Result"
column for the values. Also "Year"
column to be skipped.Then the call would look like something below:
use strict; use warnings;
use Text::CSV::Pivot;
Text::CSV::Pivot->new({ input_file => 'sample.csv',
output_file => 'output.csv',
col_key_idx => 0,
col_name_idx => 1,
col_value_idx => 2,
col_skip_idx => [3] })->transform;
METHODS
transform()
Tranform the source csv into the corresponding pivot csv based on the data passed to the constructor.
AUTHOR
Mohammad S Anwar, <mohammad.anwar at yahoo.com>
REPOSITORY
https://github.com/manwar/Text-CSV-Pivot
SEE ALSO
BUGS
Please report any bugs / feature requests to bug-text-csv-pivot at rt.cpan.org
or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Text-CSV-Pivot. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Text::CSV::Pivot
You can also look for information at:
RT: CPAN's request tracker
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
Search CPAN
LICENSE AND COPYRIGHT
Copyright (C) 2018 Mohammad S Anwar.
This program is free software; you can redistribute it and / or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:
http://www.perlfoundation.org/artistic_license_2_0
Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License.By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.
If your Modified Version has been derived from a Modified Version made by someone other than you,you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.
This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.
This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement,then this Artistic License to you shall terminate on the date that such litigation is filed.
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.