Update in requirements is a game-changer. See discussion and code furhter below.
One way, with originally posted integer data (see below for updated requirements)
use warnings;
use strict;
use feature 'say';
use List::Util qw(max min);
# =================================
# Data posted originally (integer):
# in code; with a negative value added; in Excel graph
# ======================================================
my @vals = (7,9,2,0,1,2,4,3,9);
#my @vals = (7,9,2,0,1,2,4,3,-2,9);
#my @vals = (38, 32, 28, 29, 34, 31, 15, 43, 43, 11, 4, 34);
my $max_y = max @vals;
my $min_y = min @vals;
my $min_y_to_show = ($min_y >= 0) ? 1 : $min_y;
for my $y (reverse $min_y_to_show .. $max_y) {
printf "%2d | ", $y; # y-axis: value for this row (and "axis")
say join '',
map { $_ >= $y ? ' * ' : ' 'x3 } @vals;
}
# x-axis, with its values
say ' 'x4, '-'x(3*@vals);
say ' 'x4, join '', map { sprintf "%3d", $_ } 1..@vals;
Prints
9 | * *
8 | * *
7 | * * *
6 | * * *
5 | * * *
4 | * * * *
3 | * * * * *
2 | * * * * * * *
1 | * * * * * * * *
---------------------------
1 2 3 4 5 6 7 8 9
I've made a few presentational choices of substance: to always plot down to 1 (even if all data are greater) and to not show zero -- unless there are negative values, when all is shown (add a negative value to @vals to test). These are changed fairly easily.
There's also some trivial formatting choices, for layout/spacing etc.
Otherwise there isn't anything manual really. Change @vals to plot a different data set,† hopefully in the same style. This wasn't tested much.
Update in the question introduces floating point (decimal) values. This is further elaborated in comments, what altogether amounts to a library-grade project. And some of these wants are just not possible in ASCII in a terminal, where "plotting" goes by character and we only have a 100 or so. Here is code updated for what is feasible here, and some discussion.
To accommodate floating point values (with one digit of precision we are told), the y-axis now need be plotted in smaller increments ("divisions" -- "ticks"), lest we fail to show a lot of data if they are lumped within an integer.
Then, how to divide it? Below I show all data within 20 rows, and with a row for the smallest value added if needed. From that a division is worked out, for the given data set (updated in the question). If the data are clustered around some value far from zero then this isn't good of course (imagine data between 2.8 and 3.9, going by 0.1; why would we plot bars all the way from zero?). But one has to make decisions for a given data set, what can be done automatically as well.
This necessarily leads to some imprecision in how data is shown. Showing every data point correctly isn't feasible in general in a terminal.
use warnings;
use strict;
use feature 'say';
use List::Util qw(max min);
my @vals = (0.7, 0.9, 2, 0.1, 1.2, 2.4, 0.4, 3.5, 4.9);
my $n_rows = 20;
my $max_y = max @vals;
my $min_y = min @vals;
# Show from at least the smallest y-division ("tick");
# at first use 0 and then work out the "tick" and adjust
my $min_y_to_show = $min_y >= 0 ? 0 : $min_y;
my $y_tick = ($max_y - $min_y_to_show) / $n_rows;
# Now once we have the y-division ("tick") adjust
$min_y_to_show = $min_y >= $y_tick ? $y_tick : $min_y;
say "Smallest division for y = $y_tick\n";
my @y_axis = map { $y_tick * $_ } 1 .. $n_rows;
unshift @y_axis, $min_y_to_show if $min_y_to_show < $y_axis[0];
for my $y (reverse @y_axis) {
printf "%4.2f | ", $y;
say join '',
map { $_ >= $y ? ' * ' : ' 'x3 } @vals;
}
say ' 'x6, '-'x(3*@vals);
say ' 'x6, join '', map { sprintf "%3d", $_ } 1..@vals;
Prints
Smallest division for y = 0.245
4.90 | *
4.66 | *
4.41 | *
4.17 | *
3.92 | *
3.68 | *
3.43 | * *
3.19 | * *
2.94 | * *
2.70 | * *
2.45 | * *
2.21 | * * *
1.96 | * * * *
1.72 | * * * *
1.47 | * * * *
1.23 | * * * *
0.98 | * * * * *
0.74 | * * * * * *
0.49 | * * * * * * *
0.25 | * * * * * * * *
0.10 | * * * * * * * * *
---------------------------
1 2 3 4 5 6 7 8 9
In further discussion in comments it is explained that x-values may actually go into hundreds. That would have to be scaled (can't show 500 data points in a 100-char wide terminal) but then that comes with further decisions to make since not all data can be shown.
This amounts to much too much for a Stackoverflow Q-A. There are just too many details to be specified and decided on. Hopefully the discussion and code above is helpful for people to work out more elaborate scenarios.
Finally, if all this adds up to too much I can recommend gnuplot used out of Perl. It produces publication quality plots and it's fairly simple to use for simple things -- once learned, what isn't a terrible task with all the resources and example out there.
Otherwise, there are a number of other Perl libraries for graphing of various kinds.
† This is for data shown in the original version of the question (seen in the code here)
With the values picked from the image of an Excel graph shown in the question, instead of the @vals used above (from the question's code), it prints
43 | * *
42 | * *
41 | * *
40 | * *
39 | * *
38 | * * *
37 | * * *
36 | * * *
35 | * * *
34 | * * * * *
33 | * * * * *
32 | * * * * * *
31 | * * * * * * *
30 | * * * * * * *
29 | * * * * * * * *
28 | * * * * * * * * *
27 | * * * * * * * * *
26 | * * * * * * * * *
25 | * * * * * * * * *
24 | * * * * * * * * *
23 | * * * * * * * * *
22 | * * * * * * * * *
21 | * * * * * * * * *
20 | * * * * * * * * *
19 | * * * * * * * * *
18 | * * * * * * * * *
17 | * * * * * * * * *
16 | * * * * * * * * *
15 | * * * * * * * * * *
14 | * * * * * * * * * *
13 | * * * * * * * * * *
12 | * * * * * * * * * *
11 | * * * * * * * * * * *
10 | * * * * * * * * * * *
9 | * * * * * * * * * * *
8 | * * * * * * * * * * *
7 | * * * * * * * * * * *
6 | * * * * * * * * * * *
5 | * * * * * * * * * * *
4 | * * * * * * * * * * * *
3 | * * * * * * * * * * * *
2 | * * * * * * * * * * * *
1 | * * * * * * * * * * * *
------------------------------------
1 2 3 4 5 6 7 8 9 10 11 12