LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 10-16-2018, 11:39 AM   #1
Bartonsen
Member
 
Registered: Oct 2016
Posts: 41

Rep: Reputation: Disabled
Muliply a range of numbers by a factor


I have a CSV file "test.txt" that looks like this:

201801 ; Rose ; 12 ; 7; 4 ; 0 ; 281 ; 32 ; etc
201801 ; Edwin ; 10 ; 8; 1 ; 9 ; 9 ; 60 ; etc
201802 ; Pearl ; 1 ; 1; 1 ; 39 ; 608 ; ; ; 7 etc

I want to modify this file so that all numbers from position 3 until end of line (28..31 numbers, ie days of month) are multiplied by a factor 9.5, so that the output looks like the below. Note that some days might lack a value. In that case output shall be 0.

Output:
201801 ; Rose ; 114 ; 66.5; 38 ; 0 ; 2669.5 ; 304 ; etc ...
201801 ; Edwin ; 95 ; 76; 9.5 ; 85.5 ; 85.5 ; 570 ; etc ...
201802 ; Pearl ; 9.5 ; 9.5; 9.5 ; 370.5 ; 5776 ; 0 ; 0 ; 66.5 etc

Any ideas?
 
Old 10-16-2018, 12:27 PM   #2
bradvan
Member
 
Registered: Mar 2009
Posts: 367

Rep: Reputation: 61
I think this should do it:
Code:
#!/usr/bin/perl
use strict;
use warnings;
my $file = 'test.txt';
my $outfile = 'test-out.txt';
open (my $fh, $file) or die "Could not open file, '$file' $!";
open (my $wfh, '>', $outfile) or die "Could not open file, '$outfile' $!";
while (my $row = <$fh>) {
   my @values = split(';', $row );
   for (my $i=2; $i<8; $i++) {
      if ( $values[$i] eq '' ){
         $values[$i] = 0;
      }
      $values[$i] = $values[$i] * 9.5;
   }
   for (my $i=0; $i<8; $i++) {
      printf $wfh "$values[$i] ; ";
   }
   printf $wfh "$values[8]";
}
close $fh;
close $wfh;
 
Old 10-16-2018, 12:40 PM   #3
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,883
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
Moved: This thread is more suitable in Programming and has been moved accordingly to help your thread/question get the exposure it deserves.
 
Old 10-16-2018, 12:42 PM   #4
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,883
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
My first reply would have been "What have you tried? And what avenue (or language) did you wish to pursue to solve this? Along with what is your expertise with scripting languages or other?"

But it appears that a proposed solution has been posted.

Please try that out and update your fellow forum members if you feel that solved your problem, or if you have further questions.
 
1 members found this post helpful.
Old 10-16-2018, 02:54 PM   #5
Bartonsen
Member
 
Registered: Oct 2016
Posts: 41

Original Poster
Rep: Reputation: Disabled
Thanks for the replies.
The reason I didn't say anything about what I've tried here, is that I didn't know how to approach this problem.
I'm a little bit familiar with awk and sed, and that would have been my avenue to pursue to solve this, if it would have been do-able.
But perl will work as well, so thanks a lot for the perl suggestion.

I did try it out, and found a few things I would like to comment:

1) I replaced the value 8 in the script with a higher value (38), since the number of words on each line is "2 + the number of days in month", ie. between 30 and 33 numbers in total per line. Is it ok with a high number like 38, that is higher than the exact number of words in the line?

2) An empty value (ie only space between to semicolons) seems not to be acceptable? Got error:
Argument " " isn't numeric in multiplication (*) at ./script.pl line 14, <$fh> line 3.
(I think I can find ways to get around this error, e.g. by search and replace "; ;" with ";0;".

3) The output test-out.txt seems to all come on 1 line? How can I get the output on 3 lines (as in the input file)? I have of course more than 3 lines, but you get the idea...

Thanks!
 
Old 10-16-2018, 05:09 PM   #6
individual
Member
 
Registered: Jul 2018
Posts: 315
Blog Entries: 1

Rep: Reputation: 233Reputation: 233Reputation: 233
Here is my version that updates the input file.
Code:
#!/usr/bin/env perl

use strict;
use warnings;
use constant FILE => 'file.csv';

my @rows;

open my $fh, '<', FILE or die $!;
while (my $row = <$fh>) {
    my @columns = split(/\s?;\s?/, $row);
    my @values = map { /^[0-9]+$/ ? ($_ or 0) * 9.5 : $_ } splice(@columns, 2);
    unshift @values, $columns[1];
    unshift @values, $columns[0];
    push @rows, join(';', @values);
}
close $fh;

open $fh, '>', FILE or die $!;
for my $row (@rows) {
    print $fh "$row\n";
}
close $fh;
The output is:
Code:
201801;Rose;114;66.5;38;0;2669.5;304
201801;Edwin;95;76;9.5;85.5;85.5;570
201802;Pearl;9.5;9.5;9.5;370.5;5776;;;66.5
EDIT: You can also get fancy and modify the row in-place. This version was just for my amusement, but you can still use it for your purpose.
Code:
#!/usr/bin/env perl

use strict;
use warnings;
use constant FILE => 'file.csv';

my @rows;

open my $fh, '<', FILE or die $!;
while (my $row = <$fh>) {
    my @columns = split(/\s?;\s?/, $row);
    @columns[2 .. $#columns] = map { /^[0-9]+$/ ? ($_ or 0) * 9.5 : $_ } @columns[2 .. $#columns];
    push @rows, join(' ; ', @columns);
}
close $fh;

open $fh, '>', FILE or die $!;
for my $row (@rows) {
    print $fh "$row\n";
}
close $fh;

Last edited by individual; 10-16-2018 at 05:31 PM. Reason: Removed unnecessary string concatenation.
 
1 members found this post helpful.
Old 10-16-2018, 09:36 PM   #7
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Mint 17.3
Posts: 1,881

Rep: Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660
With this InFile ...
Code:
201801 ; Rose ; 12 ; 7; 4 ; 0 ; 281 ; 32
201801 ; Edwin ; 10 ; 8; 1 ; 9 ; 9 ; 60
201802 ; Pearl ; 1 ; 1; 1 ; 39 ; 608 ; ; ; 7
... this awk ...
Code:
awk -F";" '{for (j=3;j<=NF;j++) $j=9.5*$j;
  gsub(/ +/," ; "); print}' $InFile >$OutFile
... produced this OutFile ...
Code:
201801 ; Rose ; 114 ; 66.5 ; 38 ; 0 ; 2669.5 ; 304
201801 ; Edwin ; 95 ; 76 ; 9.5 ; 85.5 ; 85.5 ; 570
201802 ; Pearl ; 9.5 ; 9.5 ; 9.5 ; 370.5 ; 5776 ; 0 ; 0 ; 66.5
Daniel B. Martin

.
 
3 members found this post helpful.
Old 10-17-2018, 01:52 AM   #8
Bartonsen
Member
 
Registered: Oct 2016
Posts: 41

Original Poster
Rep: Reputation: Disabled
Fantastic awk!
Thanks Daniel!!!
 
Old 10-17-2018, 09:06 AM   #9
danielbmartin
Senior Member
 
Registered: Apr 2010
Location: Apex, NC, USA
Distribution: Mint 17.3
Posts: 1,881

Rep: Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660Reputation: 660
The gsub used in the solution shown in post #7 is not necessary.
This is a streamlined version ...
Code:
awk '{for (j=3;j<=NF;j++) $j=9.5*$j}1' FS=OFS=" ; " $InFile >$OutFile
Daniel B. Martin

.

Last edited by danielbmartin; 10-17-2018 at 09:37 AM. Reason: Cosmetic improvement
 
1 members found this post helpful.
Old 10-17-2018, 10:42 AM   #10
Bartonsen
Member
 
Registered: Oct 2016
Posts: 41

Original Poster
Rep: Reputation: Disabled
Thanks Daniel!
I'll try it out tomorrow.
 
Old 10-17-2018, 12:14 PM   #11
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,137
Blog Entries: 6

Rep: Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826
With bash and friends, modify however you want.

Code:
data="
201801 ; Rose ; 12 ; 7; 4 ; 0 ; 281 ; 32 ; etc 
201801 ; Edwin ; 10 ; 8; 1 ; 9 ; 9 ; 60 ; etc
201802 ; Pearl ; 1 ; 1; 1 ; 39 ; 608 ; ; ; 7 etc
"

for words in $data; do
    words=$(cut -d ";" -f1 <<< "$words")
    if [[ "$words" =~ ^2018 ]]; then
        printf "%s" ""$words" ;"
    elif [[ "$words" =~ ^[1-9] ]]; then
        math=$((${words}00 * 95 / 10))
        printf "%s" "${math:0:-2}.${math: -2} ;"
    elif [[ "$words" == "etc" ]]; then
        echo "$words"
    else
        printf "%s" ""$words" "
    fi
done <<< "$data"
 
1 members found this post helpful.
Old 10-18-2018, 04:29 AM   #12
bradvan
Member
 
Registered: Mar 2009
Posts: 367

Rep: Reputation: 61
Isn't comp sci great? Always more than one way to skin a cat!
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] awk with a range of numbers? Springs Linux - Newbie 2 10-06-2017 03:59 PM
[SOLVED] [GREP] range of numbers czezz Programming 13 09-24-2015 12:43 PM
To get a range of prime numbers som_kurian Programming 12 11-18-2009 04:15 AM
Is OS a factor in wireless range? newmarket Linux - Wireless Networking 1 05-26-2006 10:50 AM
Shell scripting - Random numbers within a range felixc Linux - Newbie 2 10-09-2005 05:41 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 03:19 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration