Recent

Author Topic: Stem and Leaf Plot  (Read 10186 times)

TRon

  • Hero Member
  • *****
  • Posts: 4377
Re: Stem and Leaf Plot
« Reply #45 on: October 17, 2023, 08:46:35 am »
Thank you,
You're welcome but instead like to thank you and Boleeman for the question and idea's for approaching the issue.

Quote
I really enjoy learning Pascal Lazarus, and this forum is a great source of inspiration.
It sure can be at times  :)

All it takes is an interesting enough question that poses a programmatic dilemma.

Because how are you going to approach this ?

Boleeman's initial idea to use an internal storage for the results is in basics a good one. We all understand that is how we would have done it if we would have to use pen and paper.

Your own implemented solution is more interesting because it provides us with logic/math related issues.

Sure, after we've written the routine to correctly handle the positive values it can easily be shaped into doing the same thing (properly) for negative values.

For the interested,
Code: [Select]
if (Number > 10 * stem) then Continue;
if Number <= 10 * (stem - 1) then Break;

But with such approach we create two issues.
1. the leaves for negative values are printed in reverse order (one of the characteristics of the stemplot, some documentation states that is the correct course of action while other documentation states it is wrong. For now, I've sided with the latter and believe that the leaves should be printed in normal number order from low to high which means reverse traversal in the numbers).

2. We've introduced a problem with handling numbers with a stem of zero. It is a main issue with a stemplot table because it is in the nature of the table to have both a positive and negative zero stem. But there the standard for stemplots (in case there is any) is going haywire and I have encountered at least 4 different presentations for leaves with a stem of zero.

Besides the presentation/output for zero stems there is also the matter of how to actually approach and implement it (no matter how you wish to print it).

So you got both a programmatic and a presentation dilemma. The former being more of a logical math issue is (at least for me) the most interesting one.

Of course implementing this with help of the Pascal language makes it all the more interesting  ;)
Today is tomorrow's yesterday.

TRon

  • Hero Member
  • *****
  • Posts: 4377
Re: Stem and Leaf Plot
« Reply #46 on: October 24, 2023, 04:26:32 pm »
Quoted from another thread.

For the Stem and Leaf plot I was thinking of changing the interval in lots of 5, but wlll leave that for another day.
0   | 2 3
0* | 4 7
1   | 5
1* | 2
Dividing stems between lots of 5 (and only 5) is not the most difficult feature to implement in the existing stemplot routine.

However, and you might have done so by accident, the way you presented the example as per your quote, is headache-worthy  :)

I do hope you meant to write:

0  | 2 3 4
0* | 7
1  | 2
1* | 5

Because that actually splits up the lower five leaves 0..4 and upper five leaves 5..9 into two separate stems.

In case that solution is not what you had in mind, then please do mention it and try to explain in more detail how you want to split things up exactly because in that case I have no idea whatsoever (hence headache-worthy  :D ).

Now, let's take a look at the current implementation:
Code: Pascal  [Select][+][-]
  1. procedure StemAndLeafPlot(numbers: TCardinalList);
  2. var
  3.   number, stem, stemMin, stemMax: cardinal;
  4.   str: string;
  5. begin
  6.   numbers.Sort;
  7.  
  8.   stemMin := numbers.First Div 10;
  9.   stemMax := numbers.Last  Div 10;
  10.  
  11.   for stem := stemMin to stemMax do
  12.   begin
  13.     Str := Format('%3d | ', [stem]);
  14.     for number in numbers do
  15.     begin
  16.       if number < 10 * stem
  17.         then Continue;
  18.  
  19.       if number >= 10 * (stem + 1)
  20.         then Break;
  21.  
  22.       Str := Str + Format('%d ', [number mod 10]);
  23.     end;
  24.     Writeln(Str);
  25.   end;
  26. end;
  27.  

Because the goal is to split the leaves into a upper and lower half it becomes immediate evident that we do not actually have a leaf variable (as it was not necessary).

It isn't actually necessary to have such a separate variable (we can still use number mod 10) but it will make things easier to read/understand later on.

So, let's start with adding and using the variable leaf that holds the current leaf and while we are at it let's rename the variable Str to stemLeaves (as the string represents the leaves belonging to a certain stem).

Code: Pascal  [Select][+][-]
  1. procedure StemAndLeafPlot(numbers: TCardinalList);
  2. var
  3.   number, stem, leaf, stemMin, stemMax: cardinal;
  4.   stemLeaves: string;
  5. begin
  6.   numbers.Sort;
  7.  
  8.   stemMin := numbers.First Div 10;
  9.   stemMax := numbers.Last  Div 10;
  10.  
  11.   for stem := stemMin to stemMax do
  12.   begin
  13.     stemLeaves := Format('%3d | ', [stem]);
  14.     for number in numbers do
  15.     begin
  16.       if number < 10 * stem
  17.         then Continue;
  18.  
  19.       if number >= 10 * (stem + 1)
  20.         then Break;
  21.  
  22.       leaf := number mod 10;
  23.       stemLeaves := stemLeaves + Format('%d ', [leaf]);
  24.     end;
  25.     Writeln(stemLeaves);
  26.   end;
  27. end;
  28.  

Now it becomes more evident that whenever it is required to split up the leaf into separate parts that it is possible to check the value of the leaf and in case the leaf number fits into a certain range (let's say 0..4, which represent the lower half) that it can be checked against such range and only add those leaves to stemLeaves variable when the value of the leaf fits into/belong to that range.

You can easily verify that with the above code by replacing
Code: Pascal  [Select][+][-]
  1. stemLeaves := stemLeaves + Format('%d ', [leaf]);
with
Code: Pascal  [Select][+][-]
  1. if leaf < 5 then stemLeaves := stemLeaves + Format('%d ', [leaf]);

We can see that this works as expected and the same can be verified for choosing the upper half:
Code: Pascal  [Select][+][-]
  1. if leaf > 4 then stemLeaves := stemLeaves + Format('%d ', [leaf]);

However, currently the routine does not have any proficiency for keeping track of two separate halves (and we need that in order to be able to printout two separate halves) thus we need to accommodate for that:

Code: Pascal  [Select][+][-]
  1. procedure StemAndLeafPlot(numbers: TCardinalList);
  2. var
  3.   number, stem, leaf, stemMin, stemMax: cardinal;
  4.   loStemLeaves, hiStemLeaves: string;
  5. begin
  6.   numbers.Sort;
  7.  
  8.   stemMin := numbers.First Div 10;
  9.   stemMax := numbers.Last  Div 10;
  10.  
  11.   for stem := stemMin to stemMax do
  12.   begin
  13.     loStemLeaves := Format('%3d.0 | ', [stem]);
  14.     hiStemLeaves := Format('%3d.5 | ', [stem]);
  15.  
  16.     for number in numbers do
  17.     begin
  18.       if number < 10 * stem
  19.         then Continue;
  20.  
  21.       if number >= 10 * (stem + 1)
  22.         then Break;
  23.  
  24.       leaf := number mod 10;
  25.  
  26.       if leaf < 5
  27.         then loStemLeaves := loStemLeaves + Format('%d ', [leaf])
  28.         else hiStemLeaves := hiStemLeaves + Format('%d ', [leaf]);
  29.     end;
  30.  
  31.     Writeln(loStemLeaves);
  32.     Writeln(hiStemLeaves);
  33.   end;
  34. end;
  35.  

Thus we added another string to keep track of the stem and it's leaves and renamed the other one: loStemLeaves and hiStemLeaves.

Then for both these strings we added the initialization (note the additional ".0" and ".5 which were added to the initialization string. You can change that to whatever floats your boat).

Then we added the check for upper and lower half of the leaves and added the current leaf to the corresponding stemLeave string.

The last step requires us to write out both loStemLeaves and hiStemLeaves strings (in that particular order, though you could do it in reverse in case that makes sense for you) to match the required/wanted output.

Of course you can add an additional check whether or not you want the routine to behave in the old implemented or new implemented way so that you can choose the behaviour for the StemAndLeafPlot routine. I leave that up for/to you to implement  :D

Also note that whatever approach I took to come up with a solution for your question that /it is not the only way/ to approach/implement this.

Attached is the complete updated Free Pascal console test project.

Does that fulfill your requirements ?
« Last Edit: October 24, 2023, 04:34:48 pm by TRon »
Today is tomorrow's yesterday.

Boleeman

  • Hero Member
  • *****
  • Posts: 1158
Re: Stem and Leaf Plot
« Reply #47 on: October 25, 2023, 09:56:22 am »
Thankyou TRon for that extra split in groups of 5 feature.
Very impressed and grateful of your reply. I tried it out and it works well.
Got it to work and will work through the steps a few more times. I like how you went each of the steps.

So happy to investigate the groups of 5's solution. Much appreciated TRon.


I was pondering about how to change for groups of 2 with 5 parts to each 1 row like:

0  | 8 9
1  | 0 0 1
1*| 2
1*| 4 5 5 5
1*| 6 7
1*| 8 8 9

2  | 0 1 1

     leaf := number mod 10;
 
      if (leaf < 2) and leaf.IsInRange(0, 1)
        then loStemLeaves1 := loStemLeaves1 + Format('%d ', [leaf])
      else if (leaf < 4) and leaf.IsInRange(2, 3) then
        then loStemLeaves2 := loStemLeaves2 + Format('%d ', [leaf])
      else if (leaf < 6) and leaf.IsInRange(4, 5) then
        then loStemLeaves3 := loStemLeaves3 + Format('%d ', [leaf])
      else
       loStemLeaves4 := loStemLeaves4 + Format('%d ', [leaf])


etc.

or IsInRange?

leaf.IsInRange(0, 1)
leaf.IsInRange(2, 3)

or perhaps a case statement?

  case leaf of
    0, 1: then loStemLeaves1 := loStemLeaves1 + Format('%d ', [leaf]);
    2, 3: then loStemLeaves2 := loStemLeaves2 + Format('%d ', [leaf]);
    4, 5: then ...

Not sure. Maybe a different method?
« Last Edit: October 27, 2023, 06:28:15 am by Boleeman »

 

TinyPortal © 2005-2018