Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Plot Graph in Console by printing special character say * and spaces using matrix structure in Perl

by programmingzeal (Beadle)
on Sep 10, 2022 at 03:22 UTC ( [id://11146802]=perlquestion: print w/replies, xml ) Need Help??

programmingzeal has asked for the wisdom of the Perl Monks concerning the following question:

My task is to plot Least Mean Squared Error (LSME) values from each iteration of a machine learning algorithm in a Graph of X and Y axes/coordinates. I decided to print special character (say *) on the console using loops. I do not want to use any libraries for graph plotting but to be simple by printing sequence of special character so that I may be able to print first quadrant of X-Y coordinates onto console.

I am familiar with NDC to view port mapping in graphics programming. But I am unable to implement such nested loops that print my required graph in first quadrant on console as same that we draw on paper.

On console, the origin (0,0) is top left corner of console. But on paper the origin is left bottom if we only plot first quadrant. For overcoming this problem I cracked an idea that I use a 2 D matrix structure and some transpose operation of it and use characters (BLANK SPACE and *) for plotting my graph. I developed following code which has two arrays, one with error values (LMSE) and the other one with the count of spaces.

use strict; use warnings; use Data::Dumper; $|= 0; my @values = (7,9,2,0,1,2,4,3,9); my @values2; my $XAxis_LMSE = scalar @values; my ($minLMSE_Graph, $maxLMSE_Graph) = (sort {$a <=> $b} @values)[0, -1 +]; for (my $i = 0; $i < scalar @values; $i++) { my $rem = $maxLMSE_Graph - $values[$i]; push (@values2, $rem); } print Dumper @values2;
I computed maximum value of my error values array and assigned the difference of Max value with original error value to another array. The logic which I am able to conceive is that I fill a matrix with spaces and * which when printed on console depict a X-Y first quadrant graph on console. Is my approach promising? Can somebody confirm my approach is correct and how to build such a matrix of " " and "*" characters?

Y(x) values are given by array @values and X is number of Iterations. Iterations can go from 1 to say 100. While Y(x) also remains an Integer. Its a simple Column Bar Graph. It will be a vertical Bar Graph.

  • Comment on Plot Graph in Console by printing special character say * and spaces using matrix structure in Perl
  • Download Code

Replies are listed 'Best First'.
Re: Plot Graph in Console by printing special character say * and spaces using matrix structure in Perl
by tybalt89 (Monsignor) on Sep 10, 2022 at 04:30 UTC

    Maybe this will help get you started...

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11146802 use warnings; use List::Util qw( max ); $| = 1; my @values = (7,9,2,0,1,2,4,3,9); my ($rows, $columns) = split ' ', qx(stty size); # FIXME works on Linu +x $rows -= 2 ; # FIXME fudge my @grid = map { ' ' x $columns } 1 .. $rows; my $max = max @values; my $scale = int $rows / $max; my $wide = int $columns / @values; my $horizontal = 0; for my $value ( @values ) { for my $vert ( $rows - 1 - $value * $scale .. $rows - 1 ) { substr $grid[ $vert ], $horizontal, $wide - 1, '*' x ($wide - 1); } $horizontal += $wide; } print @grid;
      I found the alternative of `stty size` on Linux for windows console size. Just a simple perldoc -q "screen size" revealed following:

      How do I get the screen size? If you have Term::ReadKey module installed from CPAN, you can use it to fetch the width and height in characters and in pixels:

      use Term::ReadKey; my ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize();

      The code is working though I will modify it. Thanks for giving me a start!!

        $hchar is clearly reported 1 less than it should be, on Windows. Ah yes, see line 506

      Thanks for the quick response to my query. Will this code execute on windows as well? Do I have to modify it for running on windows?

        Have you tried it? Did it work for you? If it didn't work what did you do?

        We can help, but we pretty quickly get sick of holding your hand every step of the way when you show no effort!

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: Plot Graph in Console by printing special character say * and spaces using matrix structure in Perl
by kcott (Archbishop) on Sep 10, 2022 at 07:08 UTC

    G'day programmingzeal,

    I've provided some code below; it's in three parts; I don't know how much will be useful for you.

    1. See "Perl core modules". It's worth familiarising yourself with these and avoiding "(sort {$a <=> $b} @values)[0, -1]" and similar.
    2. A conversion changing the origin from top left to bottom left is very straightforward.
    3. The requirements for your graph are unclear. You start talking about an X-Y graph and then seem to be talking about a histogram. I've made a wild guess; it may be a bad one.
    #!/usr/bin/env perl use strict; use warnings; use List::Util qw{min max}; # Built-in functions my @values = (7,9,2,0,1,2,4,3,9); print 'Min: ', min(@values), "\n"; print 'Max: ', max(@values), "\n"; print 'Count: ', 0+@values, "\n"; # How to get from (0,0) at top left to bottom left my @matrix = ( [' ' x 2, '*', ' ' x 2], [' ', '*', ' ', '*', ' '], ['*', ' ' x 3, '*'], ); print "\nOriginal:\n"; for (0 .. $#matrix) { print @{$matrix[$_]}, "\n"; } print "\nInverted:\n"; for (reverse 0 .. $#matrix) { print @{$matrix[$_]}, "\n"; } # A complete guess about your graph my @graph; for (0 .. max(@values)) { $graph[$_] = [(' ') x @values]; } for my $i (0 .. $#values) { $graph[$values[$i]][$i] = '*'; } print "\nGraph:\n"; for (reverse 0 .. $#graph) { print @{$graph[$_]}, "\n"; }

    Output:

    Min: 0 Max: 9 Count: 9 Original: * * * * * Inverted: * * * * * Graph: * * * * * * * * *

    For future reference, please avoid unnecessary verbiage: trips down Memory Lane ("I recall my initial programming assignments in Java ..."); merisms ("Pyramid, Square, Rectangle, Circle"); and other irrelevances. Your code has inclusions that are never used: Data::Dumper, $XAxis_LMSE; while missing important parts, such as a print statement. A bit of ASCII art, to demonstrate the type of output you wanted, would have helped greatly.

    — Ken

      Yes, I am talking about histogram.
      Also the error values are float values of 1 decimal place, i.e., Y(X) ranging from 0.0 till say 10.0 with 0.1 increment. But Number of iterations can be fairly large like 500 till 2000 etc. Sorry about not elaborating my requirements clearly. I think we should not change origin from top left. Let it be there. Y-axis should be a horizontal line from left to right with calibration 0.0, 0.1, 0.2, 0.3 till say 10.0. While the X-axis would be from top left to bottom left with calibration from 0, 1, 2, 3, 4, 5, 6, 7, and so on till say 1000 with increment of 1. That is the ASCII Art graph should be rotated 90 degrees clockwise from its current form. Am I clear this time ? Sorry Again!!!

      For better visibility on console I need Histogram. You are right. A graph is just a plotting of x-y coordinates. In my case I have only first quadrant but I need a Histogram (For better visibility.)

        So, I think that I understand your requirements - see below. The graph is turned 90 deg to the right from what would be "normal". The width on the page is fixed at 101 characters so we don't have to worry about screen size - I think that is fine even with Windows defaults. I didn't worry about minimizing Plot memory - probably makes no difference at all.

        Added: I used a 2-D array for the input data points. The actual Plot itself is a 1D array with a string which represents all possible values of the input data (101 discrete points, 0.0-10.0 in 0.1 increments). substr() is used to manipulate individual characters within the line.

        You might want to think some more about perhaps some scaling on the "x" axis (the number of output lines could get quite large). Probably just make some plots with your real data and experiment to find new "requirements".

        Note I added the "fill" function for you to play with - can make visualizing things easier to see a solid line instead of a single point. Have fun...

        UPDATE: You might want to consider adding the data point to the graph, like this for the first point:
        (200,3.5) *******************************
        etc. Code to do that left as an exercise.

        use strict; use warnings; use List::Util qw(min max); # Simple plotting re: node_id=11146802 use constant MAXLINES => 200; #Abort if more than 200 lines in printo +ut # @inData is the input data to plot # A 2-D array of pairs (#iterations, value) # # Valid #iterations are integers. The graph will take # maxiter-miniter+1 lines to display. # The zero is suppressed. # Valid data values are in the range of 0.0-10.0 in 0.1 increments # or rather 101 characters from left of screen to the right # indicies [0..100] my @inData = ([200,3.5],[220,5.8],[210,6.5]); my @col1 = map{$_->[0]}@inData; #just extracts col1 as simple array my $min_iter = min (@col1); #these funcs are faster than a sort my $max_iter = max (@col1); print "minimum iter=$min_iter ; maximum iter= $max_iter\n"; #you decide what you want here... die "Too many lines required! ABORT!" if ($max_iter-$min_iter+1 > MAXL +INES); my @Plot; #initialize Plot Matrix # "burns" some memory at beginning of array to avoid a linear # "offset" adjustment factor # $Plot[$_]=" " x 101 for ($min_iter..$max_iter); foreach my $row_ref (@inData) { my ($n_iter, $data) = @$row_ref; plot_point ($n_iter, $data,1); #can turn FILL OFF if desired } dumpPlot(); ############ sub plot_point { my ($n_iters, $data, $fill) = @_; $fill //= 0; #fill defaults to none my $height = get_height($data); # height range 0 - 100 die "Data Value $data out of range" if ($height > 100); ## you d +ecide what to do substr($Plot[$n_iters],$height,1) = '*'; if ($fill) { substr($Plot[$n_iters],$_,1)= '*' for (0..$height-1); #don't +need a loop but #this w +as easier } } sub get_height #converts for example: 8.4543 to 85 { my $value = shift; my $rounded_value = sprintf("%.1f",$value); #.1 precision #changed +fmt spec to f (duh!) return ($rounded_value * 10); } sub dumpPlot { print "$Plot[$_]\n" for ($min_iter..$max_iter); } __END__ note lines truncated (not full 101 characters). minimum iter=200 ; maximum iter= 220 ******************************* ************************************************************* ***************************************************

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11146802]
Approved by Athanasius
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (3)
As of 2025-11-17 01:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    What's your view on AI coding assistants?





    Results (72 votes). Check out past polls.

    Notices?
    hippoepoptai's answer Re: how do I set a cookie and redirect was blessed by hippo!
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.