Stem-and-leaf plot
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
Create a well-formatted stem-and-leaf plot from the following data set, where the leaves are the last digits:
12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146
The primary intent of this task is the presentation of information. It is acceptable to hardcode the data set or characteristics of it (such as what the stems are) in the example, insofar as it is impractical to make the example generic to any data set. For example, in a computation-less language like HTML the data set may be entirely prearranged within the example; the interesting characteristics are how the proper visual formatting is arranged.
If possible, the output should not be a bitmap image. Monospaced plain text is acceptable, but do better if you can. It may be a window, i.e. not a file.
Note: If you wish to try multiple data sets, you might try this generator.
ACL2
<lang Lisp>(defun insert (x xs)
(cond ((endp xs) (list x)) ((> x (first xs)) (cons (first xs) (insert x (rest xs)))) (t (cons x xs))))
(defun isort (xs)
(if (endp xs) nil (insert (first xs) (isort (rest xs)))))
(defun stem-and-leaf-bins (xs bin curr)
(cond ((endp xs) (list curr)) ((= (floor (first xs) 10) bin) (stem-and-leaf-bins (rest xs) bin (cons (first xs) curr))) (t (cons curr (stem-and-leaf-bins (rest xs) (floor (first xs) 10) (list (first xs)))))))
(defun print-bin (bin)
(if (endp bin) nil (progn$ (cw " ~x0" (mod (first bin) 10)) (print-bin (rest bin)))))
(defun stem-and-leaf-plot-r (bins)
(if (or (endp bins) (endp (first bins))) nil (progn$ (cw "~x0 |" (floor (first (first bins)) 10)) (print-bin (first bins)) (cw "~%") (stem-and-leaf-plot-r (rest bins)))))
(defun stem-and-leaf-plot (xs)
(stem-and-leaf-plot-r (reverse (stem-and-leaf-bins (reverse (isort xs)) 0 nil))))</lang>
Ada
GNAT used for sorting, could use any other sorting method. Does not handle negative stems properly. <lang Ada> with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; with Gnat.Heap_Sort_G; procedure stemleaf is data : array(Natural Range <>) of Integer := ( 0,12,127,28,42,39,113, 42,18,44,118,44,37,113,124,37,48,127,36,29,31, 125,139,131,115,105,132,104,123,35,113,122,42,117,119,58,109,23,105, 63,27,44,105,99,41,128,121,116,125,32,61,37,127,29,113,121,58,114,126, 53,114,96,25,109,7,31,141,46,13,27,43,117,116,27,7,68,40,31,115,124,42, 128,52,71,118,117,38,27,106,33,117,116,111,40,119,47,105,57,122,109, 124,115,43,120,43,27,27,18,28,48,125,107,114,34,133,45,120, 30,127, 31,116,146); -- Position 0 is used for storage during sorting, initialized as 0
procedure Move (from, to : in Natural) is begin data(to) := data(from); end Move;
function Cmp (p1, p2 : Natural) return Boolean is begin return data(p1)<data(p2); end Cmp;
package Sorty is new GNAT.Heap_Sort_G(Move,Cmp); min,max,p,stemw: Integer; begin Sorty.Sort(data'Last); min := data(1); max := data(data'Last); stemw := Integer'Image(max)'Length; p := 1; for stem in min/10..max/10 loop put(stem,Width=>stemw); put(" |"); Leaf_Loop: while data(p)/10=stem loop put(" "); put(data(p) mod 10,Width=>1); exit Leaf_loop when p=data'Last; p := p+1; end loop Leaf_Loop; new_line; end loop; end stemleaf; </lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
AutoHotkey
<lang AutoHotkey>SetWorkingDir %A_ScriptDir%
- NoEnv
Data := "12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146"
; This loop removes the double/multiple spaces encountered when copying+pasting the given data set:
While (Instr(Data," "))
StringReplace, Data, Data,%A_Space%%A_Space%,%A_Space%,All
- Sort the data numerically using a space as the separator
Sort, Data,ND%A_Space%
OldStem := 0
- Parse the data using a space as the separator, storing each new string as A_LoopField and running the loop once per string
Loop, parse, Data,%A_Space% {
NewStem := SubStr(A_LoopField,1,StrLen(A_LoopField)-1) ; AutoHotkey doesn't have a Left() function, so this does the trick. If ( NewStem <> OldStem and StrLen(A_LoopField) <> 1) { While(OldStem+1<>NewStem) ; account for all stems which don't appear (in this example, 8) but are between the lowest and highest stems OldStem++,ToPrint .= "`n" PadStem(oldStem) ToPrint .= "`n" PadStem(NewStem) OldStem := NewStem } Else If ( StrLen(A_LoopField)=1 and !FirstStem) ToPrint .= PadStem(0),FirstStem := true ToPrint .= SubStr(A_LoopField,strLen(A_LoopField)) " " ; No Right() function either, so this returns the last character of A_LoopField (the string curently used by the parsing loop)
}
; Delete the old stem and leaf file (if any), write our new contents to it, then show it:
FileDelete Stem and leaf.txt FileAppend %ToPrint%, Stem and Leaf.txt Run Stem and leaf.txt return
PadStem(Stem){
Spaces = 0 While ( 3 - StrLen(Stem) <> Spaces ) ; If the stems are more than 2 digits long, increase the number 3 to one more than the stem length. ToReturn .= " ",Spaces++ ToReturn .= Stem ToReturn .= " | " Return ToReturn
} </lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
AWK
<lang AWK>
- syntax: GAWK -f STEM-AND-LEAF_PLOT.AWK
- sorting:
- PROCINFO["sorted_in"] is used by GAWK
- SORTTYPE is used by Thompson Automation's TAWK
BEGIN {
data = "12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 " \ "125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 " \ "105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 " \ "109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 " \ "38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 " \ "28 48 125 107 114 34 133 45 120 30 127 31 116 146" data_points = split(data,data_arr," ") for (i=1; i<=data_points; i++) { x = data_arr[i] stem = int(x / 10) leaf = x % 10 if (i == 1) { lo = hi = stem } lo = min(lo,stem) hi = max(hi,stem) arr[stem][leaf]++ } PROCINFO["sorted_in"] = "@ind_str_asc" ; SORTTYPE = 1 for (i=lo; i<=hi; i++) { printf("%4d |",i) arr[i][""] for (j in arr[i]) { for (k=1; k<=arr[i][j]; k++) { printf(" %d",j) leaves_printed++ } } printf("\n") } if (data_points == leaves_printed) { exit(0) } else { printf("error: %d data points != %d leaves printed\n",data_points,leaves_printed) exit(1) }
} function max(x,y) { return((x > y) ? x : y) } function min(x,y) { return((x < y) ? x : y) } </lang>
output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
BBC BASIC
<lang bbcbasic> INSTALL @lib$+"SORTLIB"
Sort% = FN_sortinit(0, 0) DIM Data%(120) Data%() = \ \ 12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, \ \ 37, 48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, \ \ 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, \ \ 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, \ \ 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, \ \ 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, \ \ 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, \ \ 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, \ \ 34, 133, 45, 120, 30, 127, 31, 116, 146 PROCleafplot(Data%(), DIM(Data%(),1) + 1) END DEF PROCleafplot(x%(), n%) LOCAL @%, C%, i%, j%, d% @% = 2 C% = n% CALL Sort%, x%(0) i% = x%(0) DIV 10 - 1 FOR j% = 0 TO n% - 1 d% = x%(j%) DIV 10 WHILE d% > i% i% += 1 IF j% PRINT PRINT i% " |" ; ENDWHILE PRINT x%(j%) MOD 10 ; NEXT PRINT ENDPROC</lang>
Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
C
<lang c>#include <stdio.h>
- include <stdlib.h>
int icmp(const void *a, const void *b) { return *(const int*)a < *(const int*)b ? -1 : *(const int*)a > *(const int*)b; }
void leaf_plot(int *x, int len) { int i, j, d;
qsort(x, len, sizeof(int), icmp);
i = x[0] / 10 - 1; for (j = 0; j < len; j++) { d = x[j] / 10; while (d > i) printf("%s%3d |", j ? "\n" : "", ++i); printf(" %d", x[j] % 10); } }
int main() { int data[] = { 12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146 };
leaf_plot(data, sizeof(data)/sizeof(data[0]));
return 0; }</lang>output<lang> 0 | 7 7
1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6</lang>
C++
<lang cpp>#include <algorithm>
- include <iomanip>
- include <iostream>
- include <vector>
const int dataset[] = {
12,127, 28, 42, 39,113, 42, 18, 44,118, 44, 37,113,124, 37, 48,127, 36, 29, 31,125,139,131,115,105,132,104,123, 35,113,122, 42,117,119, 58,109, 23,105, 63, 27, 44,105, 99, 41,128,121,116,125, 32, 61, 37,127, 29,113, 121, 58,114,126, 53,114, 96, 25,109, 7, 31,141, 46, 13, 27, 43,117,116, 27, 7, 68, 40, 31,115,124, 42,128, 52, 71,118,117, 38, 27,106, 33,117, 116,111, 40,119, 47,105, 57,122,109,124,115, 43,120, 43, 27, 27, 18, 28, 48,125,107,114, 34,133, 45,120, 30,127, 31,116,146
}; const int datasize = sizeof(dataset) / sizeof(dataset[0]);
int main() {
typedef std::pair<int,int> StemLeaf; std::vector<StemLeaf> stemplot;
for (int i = 0; i < datasize; ++i) { stemplot.push_back(StemLeaf(dataset[i] / 10, dataset[i] % 10)); }
std::sort(stemplot.begin(), stemplot.end()); // order stem/leaf pairs
int lo = stemplot.front().first; // minimum stem value int hi = stemplot.back().first; // maximum stem value
for (std::vector<StemLeaf>::iterator itr = stemplot.begin(); lo <= hi; ++lo) { std::cout << std::setw(2) << lo << " |"; // print stem
// while (there are more stems) and (stem is equal to lo) for ( ; itr != stemplot.end() && itr->first == lo; ++itr) { std::cout << " " << itr->second; // print leaf }
std::cout << std::endl; }
}</lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
C#
<lang csharp>using System; using System.Linq;
namespace _RC__Stem_and_leaf_plot {
internal class Program { public static void StemAndLeafPlot(int[] matrix) { int stemMax = matrix.Max()/10; int stemMin = matrix.Min()/10; Array.Sort(matrix);
for (int i = stemMin; i<=stemMax; i++) { Console.Write("{0,3} | ", i); foreach (var t in matrix) { if (t < 10 * i) continue; if (t >= 10 * (i + 1)) break; Console.Write("{0} ", t % 10); } Console.WriteLine(""); } }
private static void Main() { const string datas = "12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 "+ "125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 "+ "105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 "+ "114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 "+ "115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 "+ "105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 "+ "133 45 120 30 127 31 116 146";
var dataMatrix = datas.Split(' '); var intMatic = new int[dataMatrix.Length]; for (var i = 0; i<dataMatrix.Length; ++i) intMatic[i] = Convert.ToInt32(dataMatrix[i]);
StemAndLeafPlot(intMatic); Console.ReadKey(); } }
}</lang>
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
D
<lang d>import std.stdio, std.algorithm;
void main() {
enum data = [12,127,28,42,39,113,42,18,44,118,44,37,113,124,37,48, 127,36,29,31,125,139,131,115,105,132,104,123,35,113,122,42,117, 119,58,109,23,105,63,27,44,105,99,41,128,121,116,125,32,61,37, 127,29,113,121,58,114,126,53,114,96,25,109,7,31,141,46,13,27, 43,117,116,27,7,68,40,31,115,124,42,128,52,71,118,117,38,27, 106,33,117,116,111,40,119,47,105,57,122,109,124,115,43,120,43, 27,27,18,28,48,125,107,114,34,133,45,120,30,127,31,116,146];
int[][int] histo; foreach (x; data) histo[x / 10] ~= x % 10; immutable loHi = data.reduce!(min, max); foreach (i; loHi[0]/10 .. loHi[1]/10 + 1) writefln("%2d | %(%d %) ", i, histo.get(i, []).sort());
}</lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Euphoria
<lang euphoria>include sort.e
procedure leaf_plot(sequence s)
sequence stem s = sort(s) stem = repeat({},floor(s[$]/10)+1) for i = 1 to length(s) do stem[floor(s[i]/10)+1] &= remainder(s[i],10) end for for i = 1 to length(stem) do printf(1, "%3d | ", i-1) for j = 1 to length(stem[i]) do printf(1, "%d ", stem[i][j]) end for puts(1,'\n') end for
end procedure
constant data = { 12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124,
37, 48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146 }
leaf_plot(data)</lang>
Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
F#
<lang fsharp>open System
let data =
[ 12; 127; 28; 42; 39; 113; 42; 18; 44; 118; 44; 37; 113; 124; 37; 48; 127; 36; 29; 31; 125; 139; 131; 115; 105; 132; 104; 123; 35; 113; 122; 42; 117; 119; 58; 109; 23; 105; 63; 27; 44; 105; 99; 41; 128; 121; 116; 125; 32; 61; 37; 127; 29; 113; 121; 58; 114; 126; 53; 114; 96; 25; 109; 7; 31; 141; 46; 13; 27; 43; 117; 116; 27; 7; 68; 40; 31; 115; 124; 42; 128; 52; 71; 118; 117; 38; 27; 106; 33; 117; 116; 111; 40; 119; 47; 105; 57; 122; 109; 124; 115; 43; 120; 43; 27; 27; 18; 28; 48; 125; 107; 114; 34; 133; 45; 120; 30; 127; 31; 116; 146 ]
let plotStemAndLeafs items =
let groupedItems = items |> Seq.sort |> Seq.map (fun i -> i / 10, i % 10) |> Seq.groupBy fst let maxStem = groupedItems |> Seq.maxBy fst |> fst let stemLeafMap = Map.ofSeq groupedItems [0..maxStem] |> List.iter (fun stm -> printf " %2d | " stm match stemLeafMap.TryFind stm with | None -> () | Some items -> items |> Seq.iter (snd >> printf "%d ") printfn "")
plotStemAndLeafs data</lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Forth
<lang forth>create data
12 , 127 , 28 , 42 , 39 , 113 , 42 , 18 , 44 , 118 , 44 , 37 , 113 , 124 , 37 , 48 , 127 , 36 , 29 , 31 , 125 , 139 , 131 , 115 , 105 , 132 , 104 , 123 , 35 , 113 , 122 , 42 , 117 , 119 , 58 , 109 , 23 , 105 , 63 , 27 , 44 , 105 , 99 , 41 , 128 , 121 , 116 , 125 , 32 , 61 , 37 , 127 , 29 , 113 , 121 , 58 , 114 , 126 , 53 , 114 , 96 , 25 , 109 , 7 , 31 , 141 , 46 , 13 , 27 , 43 , 117 , 116 , 27 , 7 , 68 , 40 , 31 , 115 , 124 , 42 , 128 , 52 , 71 , 118 , 117 , 38 , 27 , 106 , 33 , 117 , 116 , 111 , 40 , 119 , 47 , 105 , 57 , 122 , 109 , 124 , 115 , 43 , 120 , 43 , 27 , 27 , 18 , 28 , 48 , 125 , 107 , 114 , 34 , 133 , 45 , 120 , 30 , 127 , 31 , 116 , 146 ,
here constant data-end
- sort ( end start -- )
over cell - swap do dup i cell+ do i @ j @ < if i @ j @ i ! j ! then cell +loop cell +loop drop ;
- plot
data-end data sort data data-end cell - @ 10 / 1+ data @ 10 / do cr i 2 u.r ." | " begin dup @ 10 /mod i = while . cell+ dup data-end = until else drop then loop drop ;
plot</lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Go
<lang go>package main
import (
"fmt" "sort" "strconv" "strings"
)
var data = `12 127 28 42` //...omitted...127 31 116 146`
func main() {
// load data into map m := make(map[int][]string) for _, s := range strings.Fields(data) { if len(s) == 1 { m[0] = append(m[0], s) } else if i, err := strconv.Atoi(s[:len(s)-1]); err == nil { m[i] = append(m[i], s[len(s)-1:]) } else { panic("non numeric data") } } // sort stem s := make([]int, len(m)) var i int for k := range m { s[i] = k i++ } sort.Ints(s) // print for k := s[0]; ; k++ { v := m[k] sort.Strings(v) fmt.Printf("%2d | %s\n", k, strings.Join(v, " ")) if k == s[len(s)-1] { break } }
}</lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Haskell
<lang haskell>import Data.List import Control.Arrow import Control.Monad
nlsRaw = "12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31"
++ " 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63" ++ " 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53" ++ " 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128" ++ " 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115" ++ " 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146"
nls :: [Int] nls = map read $ words nlsRaw
groupWith f = takeWhile(not.null). unfoldr(Just. (partition =<< (. f). (==). f. head)) justifyR = foldl ((. return) . (++) . tail) . flip replicate ' '
task ds = mapM_ (putStrLn. showStemLeaves justifyR fb. (head *** sort.concat). unzip)
$ groupWith fst $ stems ++ map (second return) stemLeaf where stemLeaf = map (`quotRem` 10) ds
stems = map (flip(,)[]) $ uncurry enumFromTo $ minimum &&& maximum $ fst $ unzip stemLeaf showStemLeaves f w (a,b) = f w (show a) ++ " |" ++ concatMap (f w. show) b fb = length $ show $ maximum $ map abs ds</lang> Output:
*Main> task nls 0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
HicEst
The dialog prompts for bitmap or a text image, and for the stem base. Data are read in from clipboard. <lang HicEst>REAL :: workspace(1000), base=16
DLG(CHeckbox=bitmap, NameEdit=base, DNum, MIn=1, MAx=16) ! 1 <= stem base <= 16 READ(ClipBoard, ItemS=nData) workspace ! get raw data
ALIAS(workspace,1, dataset,nData, stems,nData) SORT(Vector=dataset, Sorted=dataset) stems = (dataset - MOD(dataset,base)) / base dataset = dataset - base*stems max_stem = MAX(stems)
IF( bitmap ) AXIS() printed = 0 DO stem = 0, max_stem
last = INDEX(stems, stem, 4) ! option 4: search backward IF( last > printed ) THEN nLeaves = last - printed IF(bitmap) THEN LINE(PenUp=1,W=8, x=0, y=stem, x=nLeaves, y=stem) ELSE ALIAS(dataset,printed+1, leaves,nLeaves) WRITE(Format="i3, ':', 100Z2") stem, leaves ENDIF printed = printed + nLeaves ELSE WRITE(Format="i3, ':'") stem ENDIF
ENDDO</lang> Shown is the given example for bitmap=0 and base 16
0 : 7 7 C D 1 : 2 2 7 9 B B B B B B C C D D E F F F F 2 : 0 1 2 3 4 5 5 5 6 7 8 8 9 A A A A B B B C C C D E F 3 : 0 0 4 5 9 A A D F 4 : 4 7 5 : 6 : 0 3 8 9 9 9 9 A B D D D F 7 : 1 1 1 1 2 2 2 3 3 3 4 4 4 4 5 5 5 5 6 6 7 7 8 8 9 9 A A B C C C D D D E F F F F 8 : 0 0 3 4 5 B D 9 : 2
Icon and Unicon
<lang unicon>procedure main(A)
prune := integer(\A[1]) | 10 # Boundary between leaf and stem every put(data := [], integer(!&input)) writes(right(oldStem := 0,5)," |") every item := !sort(data) do { leaf := item % prune stem := item / prune while (oldStem < stem) do writes("\n",right(oldStem +:= 1, 5)," |") writes(" ",right(leaf,*prune-1,"0")) } write()
end</lang> Sample output from data.
->stem <stem.data 0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6 ->
And a second run with 2-digit leaves:
->stem 100 <stem.data 0 | 07 07 12 13 18 18 23 25 27 27 27 27 27 27 28 28 29 29 30 31 31 31 31 32 33 34 35 36 37 37 37 38 39 40 40 41 42 42 42 42 43 43 43 44 44 44 45 46 47 48 48 52 53 57 58 58 61 63 68 71 96 99 1 | 04 05 05 05 05 06 07 09 09 09 11 13 13 13 13 14 14 14 15 15 15 16 16 16 16 17 17 17 17 18 18 19 19 20 20 21 21 22 22 23 24 24 24 25 25 25 26 27 27 27 27 28 28 31 32 33 39 41 46 ->
J
Solution: (Tacit) <lang j>stem =: <.@(%&10) leaf =: 10&| stemleaf =: (stem@{. ; leaf)/.~ stem expandStems =: <./ ([ + i.@>:@-~) >./ expandLeaves=: (expandStems e. ])@[ #inv ]
showStemLeaf=: (":@,.@expandStems@[ ; ":&>@expandLeaves)&>/@(>@{. ; <@{:)@|:@stemleaf@/:~</lang>
Solution: (Explicit) <lang j>stemleafX=: monad define
leaves=. 10 | y stems=. y <.@:% 10 leaves=. stems </. leaves NB. group leaves by stem (<"0 ~.stems),.leaves
)
showStemLeafX=: monad define
'stems leaves'=. (>@{. ; <@{:)@|: stemleafX /:~ y xstems=. (<./ ([ + i.@>:@-~ ) >./) stems NB. stems including those with no leaves xleaves=. (xstems e. stems) #inv leaves NB. expand leaves to match xstems (": ,.xstems) ; ":&> xleaves
)</lang>
Example: <lang j> nls =: ; <@(_&".);._2 noun define 12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146 )
stemleaf nls NB. display has been abbreviated
┌──┬─────────────────────────────────────────────┐ │1 │2 8 3 8 │ ├──┼─────────────────────────────────────────────┤ │12│7 4 7 5 3 2 8 1 5 7 1 6 4 8 2 4 0 5 0 7 │ ├──┼─────────────────────────────────────────────┤ │2 │8 9 3 7 9 5 7 7 7 7 7 8 │ ...
showStemLeaf nls
┌──┬─────────────────────────────────────────────┐ │ 0│7 7 │ │ 1│2 3 8 8 │ │ 2│3 5 7 7 7 7 7 7 8 8 9 9 │ │ 3│0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 │ │ 4│0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 │ │ 5│2 3 7 8 8 │ │ 6│1 3 8 │ │ 7│1 │ │ 8│ │ │ 9│6 9 │ │10│4 5 5 5 5 6 7 9 9 9 │ │11│1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9│ │12│0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 │ │13│1 2 3 9 │ │14│1 6 │ └──┴─────────────────────────────────────────────┘
(showStemLeaf -: showStemLeafX) nls NB. both solutions give same result
1</lang>
Java
<lang java5>import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap;
public class StemAndLeaf { private static int[] data = { 12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146 };
public static Map<Integer, List<Integer>> createPlot(int... data){ Map<Integer, List<Integer>> plot = new TreeMap<Integer, List<Integer>>(); int highestStem = -1; //for filling in stems with no leaves for(int datum:data){ int leaf = datum % 10; int stem = datum / 10; //integer division if(stem > highestStem){ highestStem = stem; } if(plot.containsKey(stem)){ plot.get(stem).add(leaf); }else{ LinkedList<Integer> list = new LinkedList<Integer>(); list.add(leaf); plot.put(stem, list); } } if(plot.keySet().size() < highestStem + 1 /*highest stem value and 0*/ ){ for(int i = 0; i <= highestStem; i++){ if(!plot.containsKey(i)){ LinkedList<Integer> list = new LinkedList<Integer>(); plot.put(i, list); } } } return plot; }
public static void printPlot(Map<Integer, List<Integer>> plot){ for(Map.Entry<Integer, List<Integer>> line : plot.entrySet()){ Collections.sort(line.getValue()); System.out.println(line.getKey() + " | " + line.getValue()); } }
public static void main(String[] args){ Map<Integer, List<Integer>> plot = createPlot(data); printPlot(plot); } }</lang>
<lang java5>import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream;
public interface StemAndLeaf {
public static final int[] data = {12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146};
public static Map<Integer, List<Integer>> createPlot(int... data) { Map<Integer, List<Integer>> plot = Arrays.stream(data) .parallel() .boxed() .collect( Collectors.groupingBy( datum -> datum / 10, // stem, integer division Collectors.mapping( datum -> datum % 10, // leaf Collectors.toList() ) ) ) ; int highestStem = Arrays.stream(data) .parallel() .map(datum -> datum / 10) .max() .orElse(-1) //for filling in stems with no leaves ; Optional.of(plot) .map(Map::keySet) .map(Collection::size) .filter(size -> size < highestStem + 1 /*highest stem value and 0*/) .ifPresent(p -> IntStream.rangeClosed( 0, highestStem ) .parallel() .forEach(i -> plot.computeIfAbsent(i, $ -> new LinkedList<>()) ) ) ; return plot; }
public static void printPlot(Map<Integer, List<Integer>> plot) { plot.entrySet() .stream() .parallel() .peek(line -> Optional.of(line) .map(Map.Entry::getValue) .ifPresent(Collections::sort) ) .map(line -> String.join(" ", String.valueOf(line.getKey()), "|", String.valueOf(line.getValue()) ) ) .forEachOrdered(System.out::println) ; }
public static void main(String... arguments) { Optional.of(data) .map(StemAndLeaf::createPlot) .ifPresent(StemAndLeaf::printPlot) ; }
}</lang> Output:
0 | [7, 7] 1 | [2, 3, 8, 8] 2 | [3, 5, 7, 7, 7, 7, 7, 7, 8, 8, 9, 9] 3 | [0, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 7, 8, 9] 4 | [0, 0, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 6, 7, 8, 8] 5 | [2, 3, 7, 8, 8] 6 | [1, 3, 8] 7 | [1] 8 | [] 9 | [6, 9] 10 | [4, 5, 5, 5, 5, 6, 7, 9, 9, 9] 11 | [1, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9] 12 | [0, 0, 1, 1, 2, 2, 3, 4, 4, 4, 5, 5, 5, 6, 7, 7, 7, 7, 8, 8] 13 | [1, 2, 3, 9] 14 | [1, 6]
JavaScript
It turns out that HTML+CSS renders the plot quite attractively.
<lang html4strict><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > <title>stem and leaf plot</title> <script type='text/javascript'>
function has_property(obj, propname) { return typeof(obj[propname]) === "undefined" ? false : true; } function compare_numbers(a, b) {return a-b;} function stemplot(data, target) { var stem_data = {}; var all_stems = []; for (var i = 0; i < data.length; i++) { var stem = Math.floor(data[i] / 10); var leaf = Math.round(data[i] % 10); if (has_property(stem_data, stem)) { stem_data[stem].push(leaf); } else { stem_data[stem] = [leaf]; all_stems.push(stem); } } all_stems.sort(compare_numbers); var min_stem = all_stems[0]; var max_stem = all_stems[all_stems.length - 1]; var table = document.createElement('table'); for (var stem = min_stem; stem <= max_stem; stem++) { var row = document.createElement('tr'); var label = document.createElement('th'); row.appendChild(label); label.appendChild(document.createTextNode(stem)); if (has_property(stem_data, stem)) { stem_data[stem].sort(compare_numbers); for (var i = 0; i < stem_data[stem].length; i++) { var cell = document.createElement('td'); cell.appendChild(document.createTextNode(stem_data[stem][i])); row.appendChild(cell); } } table.appendChild(row); } target.appendChild(table); }
</script> <style type='text/css'>
body {font-family: monospace;} table {border-collapse: collapse;} th {border-right: 1px solid black; text-align: right;} td {text-align: right;}
</style> </head> <body>
<script type='text/javascript'>
var data = [ 12,127,28,42,39,113,42,18,44,118,44,37,113,124,37,48,127,36,29,31,125,139,131, 115,105,132,104,123,35,113,122,42,117,119,58,109,23,105,63,27,44,105,99,41,128, 121,116,125,32,61,37,127,29,113,121,58,114,126,53,114,96,25,109,7,31,141,46,13, 27,43,117,116,27,7,68,40,31,115,124,42,128,52,71,118,117,38,27,106,33,117,116, 111,40,119,47,105,57,122,109,124,115,43,120,43,27,27,18,28,48,125,107,114,34, 133,45,120,30,127,31,116,146 ]; stemplot(data, document.getElementById('target'));
</script>
</body> </html></lang>
The output looks like:
jq
<lang jq>def stem_and_leaf:
# align-right: def right: tostring | (4-length) * " " + .;
sort | .[0] as $min | .[length-1] as $max | "\($min/10|floor|right) | " as $stem | reduce .[] as $d # state: [ stem, string ] ( [ 0, $stem ]; .[0] as $stem | if ($d/10) | floor == $stem then [ $stem, (.[1] + "\($d % 10)" )] else [ $stem + 1, (.[1] + "\n\($stem+1|right) | \($d % 10)" )] end ) | .[1] ;</lang>
Example: <lang jq>def data:
[ 12,127,28,42,39,113, 42,18,44,118,44,37,113,124,37,48,127,36,29,31, 125,139,131,115,105,132,104,123,35,113,122,42,117,119,58,109,23,105, 63,27,44,105,99,41,128,121,116,125,32,61,37,127,29,113,121,58,114,126, 53,114,96,25,109,7,31,141,46,13,27,43,117,116,27,7,68,40,31,115,124,42, 128,52,71,118,117,38,27,106,33,117,116,111,40,119,47,105,57,122,109, 124,115,43,120,43,27,27,18,28,48,125,107,114,34,133,45,120, 30,127, 31,116,146 ];
data | stem_and_leaf </lang>
- Output:
<lang sh>
$ jq -n -r -f stem-and-leaf_plot.jq 0 | 77 1 | 2388 2 | 357777778899 3 | 011112345677789 4 | 001222233344456788 5 | 23788 6 | 138 7 | 1 8 | 6 9 | 9 10 | 4555567999 11 | 13333444555666677778899 12 | 00112234445556777788 13 | 1239 14 | 16</lang>
Lua
<lang lua>data = { 12,127,28,42,39,113, 42,18,44,118,44,37,113,124,37,48,127,36,29,31, 125,139,131,115,105,132,104,123,35,113,122,42,117,119,58,109,23,105, 63,27,44,105,99,41,128,121,116,125,32,61,37,127,29,113,121,58,114,126, 53,114,96,25,109,7,31,141,46,13,27,43,117,116,27,7,68,40,31,115,124,42, 128,52,71,118,117,38,27,106,33,117,116,111,40,119,47,105,57,122,109, 124,115,43,120,43,27,27,18,28,48,125,107,114,34,133,45,120, 30,127, 31,116,146
}
table.sort( data )
min, max = data[1], data[#data]
p = 1 for stem = math.floor(min/10), math.floor(max/10) do
io.write( string.format( "%2d | ", stem ) )
while data[p] ~= nil and math.floor( data[p]/10 ) == stem do io.write( string.format( "%2d ", data[p] % 10 ) ) p = p + 1 end print ""
end</lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Maple
<lang Maple>StemPlot := proc( datatable::{rtable,list,algebraic} )
local i, j, k, tf, LeafStemTable, LeafStemIndices; k:=0;
LeafStemTable := ListTools:-Categorize( (x,y) -> iquo(x, 10) = iquo(y, 10), sort(datatable));
if LeafStemTable = NULL then error "Empty List"; elif nops( [ LeafStemTable ] ) = 1 or not( type( LeafStemTable[2], list) ) then
LeafStemTable := [ LeafStemTable ];
end if;
LeafStemIndices := { seq( iquo( LeafStemTable[i][1], 10 ), i = 1..nops( [ LeafStemTable ] ) ) }; for i from min( LeafStemIndices ) to max( LeafStemIndices ) do
if i in LeafStemIndices then k := k + 1;
if i = 0 then
if min( datatable ) >=0 then printf( "%-4a%s%-s\n", i, " | ", StringTools:-Remove( "[],", convert( [seq( abs( irem( LeafStemTable[k][j], 10 ) ), j = 1..nops( LeafStemTable[k] ) )], string ) ) ); else tf := ListTools:-Occurrences( true, (x->type(x,negative))~(LeafStemTable[k])); printf( "%s%-4a%s%-s\n", "-", i, " | ", StringTools:-Remove( "[],", convert( [seq( abs( irem( LeafStemTable[k][j], 10 ) ), j = 1 .. tf )], string ) ) ); printf( "%-4a%s%-s\n", i, " | ", StringTools:-Remove( "[],", convert( [seq( abs( irem( LeafStemTable[k][j], 10 ) ), j = tf + 1 .. nops( LeafStemTable[k] ) )], string ) ) ); end if;
else
printf( "%-4a%s%-s\n", i, " | ", StringTools:-Remove( "[],", convert( [seq( abs( irem( LeafStemTable[k][j], 10 ) ), j = 1..nops( LeafStemTable[k] ) )], string ) ) );
end if;
else
printf( "%-4a%s\n", i, " | " );
end if;
end do;
return NULL;
end proc:
Y := [ 12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146];
StemPlot(Y);</lang>
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Mathematica
<lang Mathematica>len[n_] := RealDigits[n]2; padding = len[Max@ Quotient[inputdata, 10]];
For[i = Min@ Quotient[inputdata, 10],i <= Max@ Quotient[inputdata, 10], i++,
(Print[i, If[(padding - len[i]) > 0, (padding - len[i])*" " <> " |", " |"] , StringJoin[(" " <> #) & /@ Map[ToString, #]]])&@ Select[{Quotient[#, 10], Mod[#, 10]} & /@ Sort[inputdata],Part[#, 1] == i &];; , 2]</lang>
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
MATLAB / Octave
<lang Matlab>function stem_and_leaf_plot(x,stem_unit,leaf_unit)
if nargin < 2, stem_unit = 10; end; if nargin < 3, leaf_unit = 1; else x = leaf_unit*round(x/leaf_unit); end;
stem = floor(x/stem_unit); leaf = mod(x,stem_unit);
for k = min(stem):max(stem) printf('\n%d |',k) printf(' %d' ,sort(leaf(k==stem))) end; printf('\nkey:6|3=63\n'); printf('leaf unit: %.1f\n',leaf_unit); printf('stem unit: %.1f\n',stem_unit);
end;
x = [12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146];
stem_and_leaf_plot(x); </lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6 key:6|3=63 leaf unit: 1.0 stem unit: 10.0
Maxima
<lang maxima>load(descrptive)$
data: [12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48, 127,
36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146]$
stemplot(data);
0|77 1|2388 2|357777778899 3|011112345677789 4|001222233344456788 5|23788 6|138 7|1 9|69
10|4555567999 11|13333444555666677778899 12|00112234445556777788 13|1239 14|16</lang>
OCaml
The definition of the function unique
below can be omited if one uses the extlib.
<lang ocaml>let unique li =
let rec aux acc = function | [] -> (List.rev acc) | x::xs -> if List.mem x acc then aux acc xs else aux (x::acc) xs in aux [] li</lang>
<lang ocaml>let data =
[ 12; 127; 28; 42; 39; 113; 42; 18; 44; 118; 44; 37; 113; 124; 37; 48; 127; 36; 29; 31; 125; 139; 131; 115; 105; 132; 104; 123; 35; 113; 122; 42; 117; 119; 58; 109; 23; 105; 63; 27; 44; 105; 99; 41; 128; 121; 116; 125; 32; 61; 37; 127; 29; 113; 121; 58; 114; 126; 53; 114; 96; 25; 109; 7; 31; 141; 46; 13; 27; 43; 117; 116; 27; 7; 68; 40; 31; 115; 124; 42; 128; 52; 71; 118; 117; 38; 27; 106; 33; 117; 116; 111; 40; 119; 47; 105; 57; 122; 109; 124; 115; 43; 120; 43; 27; 27; 18; 28; 48; 125; 107; 114; 34; 133; 45; 120; 30; 127; 31; 116; 146 ]
let data =
List.map (fun d -> (d / 10, d mod 10)) data
let keys =
List.sort compare (unique (List.map fst data))
let () =
List.iter (fun key -> Printf.printf " %2d |" key; let vs = List.filter (fun (a,_) -> a = key) data in let vs = List.sort compare (List.map snd vs) in List.iter (Printf.printf " %d") vs; print_newline() ) keys</lang>
we can output the same latex code than the Perl example replacing the main function as follow:
<lang ocaml>let () =
print_endline "\
\\documentclass{report} \\usepackage{fullpage} \\begin{document}
\\begin{tabular}{ r | *{120}{c} }";
List.iter (fun key -> Printf.printf " %d" key; let vs = List.filter (fun (a,_) -> a = key) data in let vs = List.sort compare (List.map snd vs) in List.iter (Printf.printf " & %d") vs; print_endline " \\\\" ) keys;
print_endline "\ \\end{tabular}
\\end{document}"</lang>
Perl generating LaTeX
<lang perl>#!/usr/bin/perl -w
my @data = sort {$a <=> $b} qw( 12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 );
- FIXME: This should count the maximum number of leaves in any one stem;
- instead it takes the total number of data items, which is usually
- a massive overestimate.
my $columns = @data;
print <<"EOT"; \\documentclass{report} \\usepackage{fullpage} \\begin{document}
\\begin{tabular}{ r | *{$columns}{c} }
EOT
my $laststem = undef;
for my $value (@data) {
my $stem = int($value / 10); my $leaf = $value % 10; while (not defined $laststem or $stem > $laststem) { if (not defined $laststem) { $laststem = $stem - 1; } else { print " \\\\\n"; } $laststem++; print " $laststem"; } printf " & $leaf";
} print <<'EOT';
\end{tabular}
\end{document} EOT</lang>
LaTeX output of the Perl program:
<lang latex>\documentclass{report} \usepackage{fullpage} \begin{document}
\begin{tabular}{ r | *{120}{c} } 0 & 7 & 7 \\ 1 & 2 & 3 & 8 & 8 \\ 2 & 3 & 5 & 7 & 7 & 7 & 7 & 7 & 7 & 8 & 8 & 9 & 9 \\ ... 13 & 1 & 2 & 3 & 9 \\ 14 & 1 \end{tabular}
\end{document}</lang>
The parameter to the tabular
environment defines the columns of the table. “r” and “c” are right- and center-aligned columns, “|” is a vertical rule, and “*{count}{cols}”
repeats a column definition count times.
To get from the program above to a rendered PDF,
perl ./Stem-perl.pl > plot.tex && pdflatex plot.tex
and the output will be in plot.pdf
. Output.
Perl 6
Handles negative stems properly. <lang perl6>my @data = <
12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146
>».Int.sort;
my Int $stem_unit = 10; my %h = @data.classify: * div $stem_unit;
my $range = [minmax] %h.keys».Int; my $stem_format = "%{$range.from.chars max $range.to.chars}d";
for $range.list -> $stem {
my $leafs = %h{$stem} // []; say $stem.fmt($stem_format), ' | ', ~$leafs.map: * % $stem_unit;
}</lang>
Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
PicoLisp
<lang PicoLisp>(de *Data
12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146 )
(let L
(group (mapcar '((N) (cons (or (format (head -1 (setq N (chop N)))) 0) (last N) ) ) (sort *Data) ) ) (for I (range (caar L) (car (last L))) (prinl (align 3 I) " | " (glue " " (cdr (assoc I L)))) ) )</lang>
Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
PureBasic
PureBasic Code <lang PureBasic>If OpenConsole()
Dim MyList(120) Define i, j, StemMax, StemMin Restore MyData ; Get the address of MyData, e.g. the data to print as a Stem-and-leaf plot For a=0 To 120 Read.i MyList(a) ; Read the data into the used Array If MyList(a)>StemMax StemMax=MyList(a) ; Find the largest Stem layer at the same time EndIf If MyList(a)<StemMin StemMin=MyList(a) ; Find the smallest Stem layer at the same time EndIf Next StemMax/10: StemMin/10 ; Remove the leafs from the Stem limits SortArray(MyList(),#PB_Sort_Ascending) ; Sort the data For i=StemMin To StemMax Print(RSet(Str(i),3)+" | ") ; Print the Stem For j=0 To 120 If MyList(j)<10*i ; Skip all smaller then current Continue ElseIf MyList(j)>=10*(i+1) ; Break current print if a new Stem layer is reached Break Else Print(Str(MyList(j)%10)+" ") ; Print all Leafs on this current Stem layer EndIf Next j PrintN("") Next i Print(#CRLF$+#CRLF$+"Press ENTER to exit") Input() CloseConsole()
EndIf
DataSection MyData:
Data.i 12,127, 28, 42, 39,113, 42, 18, 44,118, 44, 37,113,124, 37, 48,127, 36, 29, 31,125,139,131,115 Data.i 105,132,104,123, 35,113,122, 42,117,119, 58,109, 23,105, 63, 27, 44,105, 99, 41,128,121,116,125 Data.i 32, 61, 37,127, 29,113,121, 58,114,126, 53,114, 96, 25,109, 7, 31,141, 46, 13, 27, 43,117,116 Data.i 27, 7, 68, 40, 31,115,124, 42,128, 52, 71,118,117, 38, 27,106, 33,117,116,111, 40,119, 47,105 Data.i 57,122,109,124,115, 43,120, 43, 27, 27, 18, 28, 48,125,107,114, 34,133, 45,120, 30,127, 31,116,146
EndDataSection</lang>
Output
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Python
Adjusting Stem.leafdigits
allows you to modify how many digits of a value are used in the leaf, with the stem intervals adjusted accordingly.
<lang python>from collections import namedtuple
from pprint import pprint as pp
from math import floor
Stem = namedtuple('Stem', 'data, leafdigits')
data0 = Stem((12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37,
48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146), 1.0)
def stemplot(stem):
d = [] interval = int(10**int(stem.leafdigits)) for data in sorted(stem.data): data = int(floor(data)) stm, lf = divmod(data,interval) d.append( (int(stm), int(lf)) ) stems, leafs = list(zip(*d)) stemwidth = max(len(str(x)) for x in stems) leafwidth = max(len(str(x)) for x in leafs) laststem, out = min(stems) - 1, [] for s,l in d: while laststem < s: laststem += 1 out.append('\n%*i |' % ( stemwidth, laststem)) out.append(' %0*i' % (leafwidth, l)) out.append('\n\nKey:\n Stem multiplier: %i\n X | Y => %i*X+Y\n' % (interval, interval)) return .join(out)
if __name__ == '__main__':
print( stemplot(data0) )</lang>
Sample Output
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6 Key: Stem multiplier: 10 X | Y => 10*X+Y
Here is an another example using an OrderedDict and Counter
<lang python>from collections import OrderedDict, Counter
x= [12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48,
127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146]
def stemleaf(x):
d = OrderedDict((((str(v)[:-1],' ')[v<10], Counter()) for v in sorted(x))) for s in ((str(v),' '+str(v))[v<10] for v in x) : d[s[:-1]][s[-1]]+=1 m=max(len(s) for s in d) for k in d: print('%s%s | %s'%(' '*(m-len(k)),k,' '.join(sorted(d[k].elements()))))
stemleaf(x) </lang>
Output :
| 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
R
<lang R> x <- c(12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146)
stem(x) </lang>
Output :
0 | 77 1 | 2388 2 | 357777778899 3 | 011112345677789 4 | 001222233344456788 5 | 23788 6 | 138 7 | 1 8 | 9 | 69 10 | 4555567999 11 | 13333444555666677778899 12 | 00112234445556777788 13 | 1239 14 | 16
Racket
<lang Racket>
- lang racket
(define (show-stem+leaf data)
(define xs (sort data <)) (for ([stem (add1 (floor (/ (last xs) 10)))]) (printf "~a|" (~a #:width 2 #:align 'right stem)) (for ([i xs]) (define-values [q r] (quotient/remainder i 10)) (when (= q stem) (printf " ~a" r))) (newline)))
(show-stem+leaf (sequence->list (in-producer read eof))) </lang>
Sample run:
$ racket sl.rkt < the-data 0| 7 7 1| 2 3 8 8 2| 3 5 7 7 7 7 7 7 8 8 9 9 3| 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4| 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5| 2 3 7 8 8 6| 1 3 8 7| 1 8| 9| 6 9 10| 4 5 5 5 5 6 7 9 9 9 11| 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12| 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13| 1 2 3 9 14| 1 6
REXX
No checks are made to see if the numbers in the data are valid numbers (negatives and decimal fractions are allowed).
Also, all numbers that are processed are normalized.
Using a sparse array bypasses the need for sorting.
<lang rexx>/*REXX program displays a stem-and-leaf plot of real numbers [-, 0, +].*/
min= /*This program handles negatives */
max= /* ··· and decimal fractions. */
parse arg data; if data= then data=, /*Not specified? Then use default*/
12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125, 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27, 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114, 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52, 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115, 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146
- .=
do j=1 for words(data); _=format(word(data,j),,0)/1 /*normalize*/ stem=left(_, max(1, length(_)-1)) /*extract the stem from the num. */ if length(_)==1 then stem=0 /*handle single-digit leaves. */ if min== then min=stem; if max== then max=stem min=min(min, stem*sign(_)); max=max(max, stem*sign(_)) leaf=right(_,1) /*pick off the leaf from the num.*/ #.stem.leaf=#.stem.leaf leaf /*construct a sorted stem-&-leaf.*/ end /*j*/
w=max(length(min),length(max)) /*width: used to align the stems.*/
do k=min to max; _=; do m=0 for 10; _=_ #.k.m; end /*m*/ say right(k,w) '│' space(_) end /*k*/ /*stick a fork in it, we're done.*/</lang>
output when using the default input:
0 │ 7 7 1 │ 2 3 8 8 2 │ 3 5 7 7 7 7 7 7 8 8 9 9 3 │ 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 │ 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 │ 2 3 7 8 8 6 │ 1 3 8 7 │ 1 8 │ 9 │ 6 9 10 │ 4 5 5 5 5 6 7 9 9 9 11 │ 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 │ 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 │ 1 2 3 9 14 │ 1 6
Ruby
This implementation will handle negative values. <lang ruby>class StemLeafPlot
def initialize(data, options = {}) opts = {:leaf_digits => 1}.merge(options) @leaf_digits = opts[:leaf_digits] @multiplier = 10 ** @leaf_digits @plot = generate_structure(data) end
private
def generate_structure(data) plot = Hash.new {|h,k| h[k] = []} data.sort.each do |value| stem, leaf = parse(value) plot[stem] << leaf end plot end
def parse(value) stem, leaf = value.abs.divmod(@multiplier) [Stem.get(stem, value), leaf.round] end
public
def print stem_width = Math.log10(@plot.keys.max_by {|s| s.value}.value).ceil + 1 Stem.get_range(@plot.keys).each do |stem| leaves = @plot[stem].inject("") {|str,leaf| str << "%*d " % [@leaf_digits, leaf]} puts "%*s | %s" % [stem_width, stem, leaves] end
puts "key: 5|4=#{5 * @multiplier + 4}" puts "leaf unit: 1" puts "stem unit: #@multiplier" end
end
class Stem
@@cache = {}
def self.get(stem_value, datum) sign = datum < 0 ? :- : :+ cache(stem_value, sign) end private def self.cache(value, sign) if @@cachevalue, sign.nil? @@cachevalue, sign = self.new(value, sign) end @@cachevalue, sign end
def initialize(value, sign) @value = value @sign = sign end public attr_accessor :value, :sign def negative? @sign == :- end
def <=>(other) if self.negative? if other.negative? other.value <=> self.value else -1 end else if other.negative? 1 else self.value <=> other.value end end end
def to_s "%s%d" % [(self.negative? ? '-' : ' '), @value] end def self.get_range(array_of_stems) min, max = array_of_stems.minmax if min.negative? if max.negative? min.value.downto(max.value).collect {|n| cache(n, :-)} else min.value.downto(0).collect {|n| cache(n, :-)} + 0.upto(max.value).collect {|n| cache(n, :+)} end else min.value.upto(max.value).collect {|n| cache(n, :+)} end end
end
data = DATA.read.split.map {|s| Float(s)} StemLeafPlot.new(data).print
__END__ 12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146</lang>
- Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6 key: 5|4=54 leaf unit: 1 stem unit: 10
Simple version <lang ruby>class StemLeafPlot
def initialize(data, leaf_digits=1) @leaf_digits = leaf_digits multiplier = 10 ** @leaf_digits @plot = data.sort.group_by{|x| x / multiplier} @plot.default = [] @plot.each{|k,v| @plot[k] = v.map{|val| val % multiplier}} end def print min, max = @plot.keys.minmax stem_width = max.to_s.size (min..max).each do |stem| leaves = @plot[stem].inject("") {|str,leaf| str << "%0*d " % [@leaf_digits, leaf]} puts "%*s | %s" % [stem_width, stem, leaves] end end
end
data = DATA.read.split.map {|s| Integer(s)} StemLeafPlot.new(data).print
__END__ 12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146</lang>
- Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Scala
<lang scala>def stemAndLeaf(numbers: List[Int]) = {
val lineFormat = "%" + (numbers map (_.toString.length) max) + "d | %s" val map = numbers groupBy (_ / 10) for (stem <- numbers.min / 10 to numbers.max / 10) { println(lineFormat format (stem, map.getOrElse(stem, Nil) map (_ % 10) sortBy identity mkString " ")) }
}</lang>
Example:
scala> val list = """12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 | 146""" split "\\s+" map (_.toInt) toList list: List[Int] = List(12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48, 127, 36, 29, 31, 125, 139, 1 31, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 1 20, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146) scala> stemAndLeaf(list) 0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Seed7
<lang seed7>$ include "seed7_05.s7i";
const proc: leafPlot (in var array integer: x) is func
local var integer: i is 0; var integer: j is 0; var integer: d is 0; begin x := sort(x); i := x[1] div 10 - 1; for key j range x do d := x[j] div 10; while d > i do if j <> 1 then writeln; end if; incr(i); write(i lpad 3 <& " |"); end while; write(" " <& x[j] rem 10); end for; writeln; end func;
const proc: main is func
local const array integer: data is [] ( 12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146); begin leafPlot(data); end func;</lang>
Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
Tcl
<lang tcl>package require Tcl 8.5
- How to process a single value, adding it to the table mapping stems to
- leaves.
proc addSLValue {tblName value {splitFactor 10}} {
upvar 1 $tblName tbl # Extract the stem and leaf if {$value < 0} {
set value [expr {round(-$value)}] set stem -[expr {$value / $splitFactor}]
} else {
set value [expr {round($value)}] set stem [expr {$value / $splitFactor}]
} if {![info exist tbl]} {
dict set tbl min $stem
} dict set tbl max $stem set leaf [expr {$value % $splitFactor}] dict lappend tbl $stem $leaf
}
- How to do the actual output of the stem-and-leaf table, given that we have
- already done the splitting into stems and leaves.
proc printSLTable {tblName} {
upvar 1 $tblName tbl # Get the range of stems set min [dict get $tbl min] set max [dict get $tbl max] # Work out how much width the stems take so everything lines up set l [expr {max([string length $min], [string length $max])}] # Print out the table for {set i $min} {$i <= $max} {incr i} {
if {![dict exist $tbl $i]} { puts [format " %*d |" $l $i] } else { puts [format " %*d | %s" $l $i [dict get $tbl $i]] }
}
}
- Assemble the parts into a full stem-and-leaf table printer.
proc printStemLeaf {dataList {splitFactor 10}} {
foreach value [lsort -real $dataList] {
addSLValue tbl $value $splitFactor
} printSLTable tbl
}
- Demo code
set data {
12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146
} printStemLeaf $data</lang> Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
TUSCRIPT
<lang tuscript> $$ MODE TUSCRIPT digits=* DATA 12 127 28 42 39 113 42 18 44 118 44 37 113 124 37 48 127 36 29 31 125 139 131 115 105 132 104 123 35 113 DATA 122 42 117 119 58 109 23 105 63 27 44 105 99 41 128 121 116 125 32 61 37 127 29 113 121 58 114 126 53 114 DATA 96 25 109 7 31 141 46 13 27 43 117 116 27 7 68 40 31 115 124 42 128 52 71 118 117 38 27 106 33 117 116 111 DATA 40 119 47 105 57 122 109 124 115 43 120 43 27 27 18 28 48 125 107 114 34 133 45 120 30 127 31 116 146
digits=SPLIT (digits,": :"), digitssort=DIGIT_SORT (digits)
SECTION format formatstem=CENTER (currentstem,5," ") PRINT formatstem, leaves ENDSECTION
leaves="",currentstem=0 LOOP d=digitssort leaf=mod(d,10),stem=d/10 IF (stem!=currentstem) THEN
DO format IF (stem!=nextstem) THEN currentstem=nextstem=nextstem+1,leaves="" DO format ENDIF
leaves=leaf, currentstem=stem ELSE
leaves=APPEND (leaves,leaf), nextstem=stem+1
ENDIF ENDLOOP DO format </lang> Output:
0 7'7 1 2'3'8'8 2 3'5'7'7'7'7'7'7'8'8'9'9 3 0'1'1'1'1'2'3'4'5'6'7'7'7'8'9 4 0'0'1'2'2'2'2'3'3'3'4'4'4'5'6'7'8'8 5 2'3'7'8'8 6 1'3'8 7 1 8 9 6'9 10 4'5'5'5'5'6'7'9'9'9 11 1'3'3'3'3'4'4'4'5'5'5'6'6'6'6'7'7'7'7'8'8'9'9 12 0'0'1'1'2'2'3'4'4'4'5'5'5'6'7'7'7'7'8'8 13 1'2'3'9 14 1'6
Ursala
<lang Ursala>#import std
- import nat
data =
<
12,127,28,42,39,113,42,18,44,118,44,37,113,124,37,48,127,36,29,31,125,139,131, 115,105,132,104,123,35,113,122,42,117,119,58,109,23,105,63,27,44,105,99,41,128, 121,116,125,32,61,37,127,29,113,121,58,114,126,53,114,96,25,109,7,31,141,46,13, 27,43,117,116,27,7,68,40,31,115,124,42,128,52,71,118,117,38,27,106,33,117,116, 111,40,119,47,105,57,122,109,124,115,43,120,43,27,27,18,28,48,125,107,114,34, 133,45,120,30,127,31,116,146>
stemleaf_plot =
^|T(~&,' | '--)*+ -+
^p(pad` @hS; * ==` ~-rlT,mat` *tS)@hSS+ (%nP*)^|*H/~& ^lrNCT/iota ~&, ^(*+ ^C/~&+ -:0!,~&zl)+ ^|(~&,nleq-<)*+ nleq-<&l@lK2hlPrSXS+ * division\10+-
- show+
main = stemleaf_plot data</lang>
Reading from right to left on the bottom line of the stemleaf_plot
function, we
obtain the quotient and remainder of every datum divided by ten, partition by
quotients, sort the partitions and sort within each partition,
make a note of the maximum quotient, and reify the table into a function.
From right to left on the line above, we apply the reified function to each number up to the maximum quotient (i.e., including 8 which wasn't in the original table), convert each number to a string, flatten each leaf list with spaces as separators, and right justify the stem list.
The remaining top line forms the concatenation of each stem with its flattened leaf list and a vertical bar between them.
output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6
zkl
<lang zkl>fcn leaf_plot(xs){
xs=xs.sort(); i := xs[0] / 10 - 1; foreach j in (xs.len()){ d := xs[j] / 10; while (d > i){ print("%s%3d |".fmt(j and "\n" or "", i+=1)); } print(" %d".fmt(xs[j] % 10)); } println();
}
data := T(
12, 127, 28, 42, 39, 113, 42, 18, 44, 118, 44, 37, 113, 124, 37, 48, 127, 36, 29, 31, 125, 139, 131, 115, 105, 132, 104, 123, 35, 113, 122, 42, 117, 119, 58, 109, 23, 105, 63, 27, 44, 105, 99, 41, 128, 121, 116, 125, 32, 61, 37, 127, 29, 113, 121, 58, 114, 126, 53, 114, 96, 25, 109, 7, 31, 141, 46, 13, 27, 43, 117, 116, 27, 7, 68, 40, 31, 115, 124, 42, 128, 52, 71, 118, 117, 38, 27, 106, 33, 117, 116, 111, 40, 119, 47, 105, 57, 122, 109, 124, 115, 43, 120, 43, 27, 27, 18, 28, 48, 125, 107, 114, 34, 133, 45, 120, 30, 127, 31, 116, 146 );
leaf_plot(data);</lang>
- Output:
0 | 7 7 1 | 2 3 8 8 2 | 3 5 7 7 7 7 7 7 8 8 9 9 3 | 0 1 1 1 1 2 3 4 5 6 7 7 7 8 9 4 | 0 0 1 2 2 2 2 3 3 3 4 4 4 5 6 7 8 8 5 | 2 3 7 8 8 6 | 1 3 8 7 | 1 8 | 9 | 6 9 10 | 4 5 5 5 5 6 7 9 9 9 11 | 1 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 12 | 0 0 1 1 2 2 3 4 4 4 5 5 5 6 7 7 7 7 8 8 13 | 1 2 3 9 14 | 1 6