Friday, June 24, 2022

Day 11/100 Frequency Graph

 Day 11/100 Frequency Graph

Picturing the most likely occurrences


So, I mentioned about doing frequency analysis for the last program, which is Happy Number. I somewhat made it like it's not a trivial thing to do. However, that is not true at all! It is trivial to count occurrences and use an array to do that. You don't even need hash to do it. Just a simple array will do!


int LoadData() {
  int num;
  char *buf;
  while (buf=fgets(Liner,MAXSTR,stdin)) {
    num=atoi(buf); Data[num]++;
    if (minEntry>num) minEntry=num;
    if (maxEntry<num) maxEntry=num;
  }
  return 0;
}


And that's it! It just doesn't get any easier than that! So, the hour of code is still on-going. What do I do with the rest of the time? Trying to make the program into some eye-catching form, of course. That means graphic.


Now, all this time I've been doing, I've been doing it text console. So, certainly there's no graphic screen involved. But not really. If you look at my Point and Figure chart program, you see graphic! Very crude, I admit, but graphic! So, what kind of other graphic can I do with a text screen? 


There are two kinds of easy chart that comes to mind: Bar chart, and Line chart. Both are easy to do. In fact, some of the early program actually charted Biorhythm. That's graphic. As per usual, I chart it sideways as to allow numerous entries. The range of the charts is fixed, but the number of entries aren't. Therefore, I let the screen scroll to as many entries as available.


The difference between the Line chart and the Bar chart is that the Line chart is only a single character, while the Bar chart is filled with graphing character. That's easy enough to do where the programming language allows you to multiply a character and come up with a string with that number of character. C doesn't have that, so I simply made my own. 


Here is the code for the Bar chart. As you can see, I use a lot of supporting functions. This would be included in libraries, had I use a more sophisticated platform. But I don't, and I don't mind. They're easy enough to do by myself.


void GraphBar(char LineMark) {
  int i,j;
  int spacer;
  DrawHeader();
  for (i=minEntry;i<=maxEntry;i++) {
    printf("%4d%5d  ",i,Data[i]);
    spacer=(int)map((float)minValue
      ,(float)Data[i],(float)maxValue
      ,0.0,72.0);
    DrawHLine(0,spacer,'X'); putchar('\n');
  }
}


As you can see, I use a function called DrawHLine() to draw horizontal line, from start to stop, with a particular character. That's easy enough to do. The current demand for graphic can be easily met just by using a horizontal drawing technique.


And here is the Line chart. As you can see, the drawing routine is slighty more complex.


void GraphLine(char LineMark) {
  int i,j; int spacer; int g0,g1; int l0,l1;
  DrawHeader();
  for (i=minEntry;i<=maxEntry;i++) {
    if (i==minEntry)
      g0=(int)map((float)minValue,
         (float)Data[i],(float)maxValue,0.0,72.0);
    g1=(int)map((float)minValue,(float)Data[i],
       (float)maxValue,0.0,72.0);
    printf("%4d%5d  ",i,g1); l0=g0; l1=g1;
    if (l0>l1) { j=l0;l0=l1;l1=j; }
    DrawHLine(0,l0,' '); DrawHLine(l0,l1,'+');
    putchar('\n'); g0=g1;
  }
}


The most complicated item in the function is map(). That function basically translate a range into another range. This is very useful in case you want to convert different values such as from meter to kilometer, Fahrenheit to Celcius, or degree to radians. Here, I'm using it to scale the data to fit the screen. It uses a lot of parameters, but conceptually, it's rather simple.


Another thing to consider is that I use different variables, g0-g1 and l0-l1. I totally forgot to separate them, but hey, nobody's perfect! Once I use the variables to separate the graph values and the line drawing values, everything works as is.


And that's about it. Some programming problems are so simple, I wonder why people keep saying that computer programming is hard. It really depends on the problem, you know?


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXENTRY 10000
#define MAXSTR 2560


char Liner[MAXSTR];
int Data[MAXENTRY];
int minEntry=MAXENTRY;
int maxEntry=0;
int minValue=MAXENTRY;
int maxValue=0;

int LoadData() {
  int num;
  char *buf;

  while (buf=fgets(Liner,MAXSTR,stdin)) {
    num=atoi(buf); Data[num]++;
    if (minEntry>num) minEntry=num;
    if (maxEntry<num) maxEntry=num;
  }
  return 0;
}

float map(float x0,float x1, float x2,float y0, float y2) {
  float y1;
  y1=(x1-x0)*(y2-y0)/(x2-x0);
  return y1;
}


void DrawHLine(int start, int stop, char mark) {
  int i;
  if (start>stop) { i=start; start=stop; stop=i; }
  for (i=start;i<=stop;i++) putchar(mark);
}

void DrawHeader() {
  printf("\n\n\n");
  DrawHLine(0,5,' ');
  printf("%5d",minValue);
  DrawHLine(15,(72-5),'-');
  printf("%5d\n",maxValue);
}

void GraphBar(char LineMark) {
  int i,j;
  int spacer;

  DrawHeader();
  for (i=minEntry;i<=maxEntry;i++) {
    printf("%4d%5d  ",i,Data[i]);
    spacer=(int)map(
      (float)minValue,(float)Data[i],(float)maxValue
      ,0.0,72.0);
    DrawHLine(0,spacer,'X'); putchar('\n');
  }
}

void GraphLine(char LineMark) {
  int i,j;
  int spacer;
  int g0,g1;
  int l0,l1;

  DrawHeader();
  for (i=minEntry;i<=maxEntry;i++) {
    if (i==minEntry)
      g0=(int)map(
      (float)minValue,(float)Data[i],(float)maxValue
      ,0.0,72.0);
    g1=(int)map(
      (float)minValue,(float)Data[i],(float)maxValue
      ,0.0,72.0);
    printf("%4d%5d  ",i,g1);
    l0=g0; l1=g1;
    if (l0>l1) { j=l0;l0=l1;l1=j; }
    DrawHLine(0,l0,' ');
    DrawHLine(l0,l1,'+');
    putchar('\n');
    g0=g1;
  }
}

int main (int argc, char *argv[] ) {
  int n,i;

  for (i=0;i<MAXENTRY;i++) Data[i]=0;
  LoadData();
  for (i=minEntry;i<=maxEntry;i++) {
    if (minValue>Data[i]) minValue=Data[i];
    if (maxValue<Data[i]) maxValue=Data[i];
    printf("%d\t%d\n",i,Data[i]);
  }
  printf("\n");
  printf("Entry: %d\t%d\n",minEntry,maxEntry);
  printf("Value: %d\t%d\n",minValue,maxValue);

  GraphBar('X');
  GraphLine('+');
  return 0;
}


One more thing: The program above is customized for the data given. You need to change it a bit to deal with generalized form of data. And if somebody tells you that doing computer graphic in a character terminal is impossible, DON'T BELIEVE IT!



No comments:

Post a Comment