1

So i am writing a program where i have to read from a file that contains coordinate points and then display the plot. For instance, i have this as the first line, 20 10. What my program should do, is plot this using a 2D array of characters. An 'X' is to be used for the points.

This is how i calculated the slope and the formula for regression line.

enter image description here

  float xMean = xSum / count;
  float yMean = ySum / count;
  float n = count;
  float slope = (xySum - n* xMean * yMean) / (xSqSum - n * xMean * xMean);

I used below method to print '-' char if there is no 'X' char and if there is then an '*' char.

for (int i = 0; i < graph.length; i++) {
      int yPred = Math.round(yMean + slope * (i - xMean)); // calculate regression value
        graph[21-1-yPred][i + 1] = graph[21-1-yPred][i + 1 ] == 'X' ? '*' : '-';
    }

Correct output that i am aiming for

Scatterplot using 2D array of characters

However i am getting this output:

enter image description here

What i am trying to achieve is that my program would print "-"s as the regression line segments, and "*"s where a line segment and a point are located at the same spot

However i am getting less dashes in my program as compared to the correct output. Also the asterisk is not in the middle where it is supposed to be.

This is the text file which i am using. I was able to get the validation done with my program. ** x-coordinates in the range [0, 40] and y-coordinates in the range [1, 20].** enter image description here

Saad
  • 399
  • 8
  • 25
  • could you explain why us that the expected output? it's a bit unclear why there should be more than one `X`s if there's only `20 10` to print – ItamarG3 Oct 31 '16 at 19:11
  • No there is a file which contains points, my job was to read the points with some validation checks. I was able to do that. I gave the `20 10` just as an example. @ItamarGreen – Saad Oct 31 '16 at 19:13
  • That outlier near 0,0.. heh - On topic though, try filling your array with ' ' (space chars) then marking the 'x' in the 2d array, then printing to console should space output closer to your goal. (You didn't specify axis, the '-' or '*' so unless specified further, can't account for that atm) – Nick Bell Oct 31 '16 at 19:18
  • x-coordinates are in the range [0, 40] and y-coordinates in the range [1, 20].", these are the bounds of my 2d array. The"-' is for regresison line and the "*" is when the point and the regression line segemnt are located at the same spot. @NickBell – Saad Oct 31 '16 at 19:21
  • I tried filling the array with space characters like this `charArray[k][d] = '\0';` however the the'X's are still on the same line. @NickBell – Saad Oct 31 '16 at 19:38
  • Can you take a look at my code one more time please and tell me what i am doing wrong? @NickBell – Saad Nov 02 '16 at 05:29
  • I have also added the text file so that it makes things simple for you. I was able to get the validation checks done through my program. I just need help with the '-' chars and the '*' chars. @NickBell – Saad Nov 02 '16 at 05:46

2 Answers2

2

Alright so let's knock out this problem! First, after spending 15 minutes or so putting lines to your screen shot text file, here's the image I drew up (depicting the original of course):

orig img with line markup

Now, you mentioned having a line or entry of an 'X' you are to draw up. This is also a text file and you're reading it in from file, so here's what said file (let's call it 'coords.txt' for the sake of this example).

20 10

enter image description here

With 20 and 10 being the coordinates read, based off the line-overlay image I made, I think that (20 10) would correlate to x-axis=21, y-axis=10 (The off by one is likely to be a simple indexing off by one error - read more here I basically just treated the graph like a graph ).

This is one way to read that point/data in from file and prepare it to be plotted: read 20 10 from file by

Scanner s = new Scanner(new File("coords.txt"));
int x = 0;
int y = 0;
if (s.hasNext())
    x = s.nextInt();
if (s.hasNext())
    y = s.nextInt();
s.close();

That code is easy enough, in that it makes a Scanner object to read the contents of our file. Then, declaring an x and y integer in order to store our coord values. Note: s.hasNext() is valuable logic in general to check to make sure there's something in the text file to read before trying to read it - you could also adapt s.hasNextInt() in order to only look ahead for an integer token next up to read from file.

However, I notice you have several X-marks-the-spot on your result graph. By going back to the image I drew up to visually see the coordinates you would likely have in your non-mentioned explicitly 'coords.txt' file, let's say in this example you would have a file with the following lines:

20 10
0 1
40 20
13 17
10 20

Now these numbers are based off that original image I drew lines over, however I respected the off-by-one error so that's why they don't directly correlate to coordinates on that image, because for the sake of correcting that off-by-one error.

Now with several coords, how to go about reading that input in a more efficient manner? Here's one way to go about it:

import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class App {
    public static final int SIZE = 100;
    public static void main(String[] args) throws IOException {
        Scanner s = new Scanner(new File("C:\\Users\\Nick\\Desktop\\coords.txt"));
        int x = 0;
        int y = 0;
        int[] xCoords = new int[SIZE];
        int[] yCoords = new int[SIZE];
        int index = 0;
        while (s.hasNextLine()) {
            if (s.hasNextInt()) {
                x = s.nextInt();
            } else {
                System.out.println("had a next line, but no integers to read");
                break;
            }
            if (s.hasNextInt()) {
                y = s.nextInt();
            } else {
                System.out.println("had a next line, but no integers to read");
                break;
            }
            xCoords[index] = x;
            yCoords[index] = y;
            index++;
        }
        s.close();
    }
}

This code assumes several things, including;

  1. we don't think we're going to read more than 100 coordinates to plot. (If you do know this number, simply edit the SIZE value to the pre-determined value. If you don't know the value, then look into growing/expanding an array or java.util.List and a tutorial on the java list interface).
  2. the xCoords and yCoords integer arrays are matching/mirror in that they should both be filled at a given index in order to represent a plot-able coordinate (X-marks-the-spot).
  3. likewise to the previous point, in the while loop, each line is thought to contain two integer values to read that resemble a coordinate.
  4. the integer index value increases after each loop to help ready the next point to read in and also let us know how many points were read in while the while loop was looping.

Running the above code for reading the 'coords.txt' file, and adding a few simple System.out.println()'s after the while loop but before the s.close(); helps show what was read in. Here's the second version of the input reading, along with the output to show what happened:

import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class App {
    public static final int SIZE = 100;
    public static void main(String[] args) throws IOException {
        Scanner s = new Scanner(new File("C:\\Users\\Nick\\Desktop\\coords.txt"));
        int x = 0;
        int y = 0;
        int[] xCoords = new int[SIZE];
        int[] yCoords = new int[SIZE];
        int index = 0;
        while (s.hasNextLine()) {
            if (s.hasNextInt()) {
                x = s.nextInt();
            } else {
                System.out.println("had a next line, but no integers to read");
                break;
            }
            if (s.hasNextInt()) {
                y = s.nextInt();
            } else {
                System.out.println("had a next line, but no integers to read");
                break;
            }
            xCoords[index] = x;
            yCoords[index] = y;
            index++;
        }
        System.out.println("Output from what was read in from file:");
        for (int i = 0; i < index; ++i) {
            System.out.print(xCoords[i] + ", ");
            System.out.println(yCoords[i]);
        }
        s.close();
    }
}

And the respective output:

had a next line, but no integers to read
Output from what was read in from file:
20, 10
0, 1
40, 20
13, 17
10, 20

The first two lines are just comments that can be removed. It's good to see we can read in our data effectively!

Moving onto the bread-and-butter part of the problem, the full output. In order to output the example image, we need to effectively print out a 2D array of characters (at least that's the idea I'll use to explain here).

We know (based on looking at the line-marked graph) that we need to have dimensions of 21 x 42 for complete replica output. This is because of the axis markings themselves. So, let's start off by declaring a 2D array like:

char[][] graph = new char[21][42];

That's great! We have an invisible graph! Let's get those crummy null characters out and put some good old spaces in! After all, this is the root cause of printing everything out all funky, you can't expect the char value of '\0' to behave the same as ' '.

for (int i = 0; i < graph.length; ++i)
    for (int j = 0; j < graph[0].length; ++j)
        graph[i][j] = ' ';

Now let's start marking it up with the bare axis. First the y-axis:

for (int i = 0; i < graph.length; ++i) {
    graph[i][0] = '/';
}
graph[20][0] = '+';

Note: we can cheat with the + symbol since it comes at the very end. The for-loop could use and if statement and mark the last index with the + but lets try to be intuitive too.

Then, for the x-axis:

for (int i = 1; i < graph[0].length; ++i) {
    graph[20][i] = '-';
}

Since we know where the very end is, we simply use the hard-coded value of 20 and loop over the index to input the x-axis symbols/char -.

Let's take a look at this newly created graph with the following code:

for (int i = 0; i < graph.length; ++i) {
    for (int j = 0; j < graph[0].length; ++j) {
        System.out.print(graph[i][j]);
    }
    System.out.println();
}

Which should produce the following output:

/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
+-----------------------------------------

Let's plot the X-marks-the-spot now, based on what you used with the coordinates from the earlier code.

This can be done by looping over the mirror integer arrays (xCoords and yCoords) and using those coords to plot onto the 2D char[][] graph.

for (int i = 0; i < index; ++i) {
    graph[21 - 1 - yCoords[i]][xCoords[i] + 1] = 'X';
}

Breaking down the information here (above), the [21 - 1 - yCoords[i]] is meant to translate the y-coordinate to the respective spot represented in the 2D array by using a offset from the top value of the graph portrayed backwards (hence using 21 to start from the top) and also another minus one offset because of the axis itself (e.g. the '/' and '-' and '+' characters respectively). For the xCoords, a simply plus one is used for the axis itself offset.

Here is that output:

/          X                             X
/                                         
/                                         
/             X                           
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                    X                    
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/                                         
/X                                        
+-----------------------------------------

Looks very like an early stage of those pictures of what the output is suppose to be!

All in all, this is what my final code looked like:

import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class App {
    public static final int SIZE = 100;
    public static void main(String[] args) throws IOException {
        Scanner s = new Scanner(new File("C:\\Users\\Nick\\Desktop\\coords.txt"));
        int x = 0;
        int y = 0;
        int[] xCoords = new int[SIZE];
        int[] yCoords = new int[SIZE];
        int index = 0;
        while (s.hasNextLine()) {
            if (s.hasNextInt()) {
                x = s.nextInt();
            } else {
                System.out.println("had a next line, but no integers to read");
                break;
            }
            if (s.hasNextInt()) {
                y = s.nextInt();
            } else {
                System.out.println("had a next line, but no integers to read");
                break;
            }
            xCoords[index] = x;
            yCoords[index] = y;
            index++;
        }
        System.out.println("Output from what was read in from file:");
        for (int i = 0; i < index; ++i) {
            System.out.print(xCoords[i] + ", ");
            System.out.println(yCoords[i]);
        }
        s.close();
        char[][] graph = new char[21][42];
        for (int i = 0; i < graph.length; ++i)
            for (int j = 0; j < graph[0].length; ++j)
                graph[i][j] = ' ';
        for (int i = 0; i < graph.length; ++i)
            graph[i][0] = '/';
        graph[20][0] = '+';
        for (int i = 1; i < graph[0].length; ++i)
            graph[20][i] = '-';
        for (int i = 0; i < index; ++i)
            graph[21 - 1 - yCoords[i]][xCoords[i] + 1] = 'X';
        for (int i = 0; i < graph.length; ++i) {
            for (int j = 0; j < graph[0].length; ++j)
                System.out.print(graph[i][j]);
            System.out.println();
        }
    }
}

If you want to include more detail, e.g. multiple '-' for regression and '*' for point, I'd encourage using this information to learn and adapt to reading those coordinates for that additional information and applying it to this example. There's no supplied information on that however, so I won't venture off topic with alleged text files and coords for that, either edit it into the question or try it yourself and learn something. :) Cheers

Community
  • 1
  • 1
Nick Bell
  • 516
  • 3
  • 16
  • Thank you. This is so informative and detailed. I'll try to learn and modify this according to my need. This again. it was very nicely explained. :) – Saad Nov 01 '16 at 15:44
  • Hey can you please help me with the regression line. I just coded this but i am not getting the correct output. It would be a waste to post the same question and you already know the question. So i will edit the question, please have a look as to what i am doing wrong. – Saad Nov 02 '16 at 05:12
  • My problem is that the '-' char stops mid-way through the graph. I have added the pic in case you want to compare. My problem is, i believe, with this line `graph[21-1-yPred][i + 1] = graph[21-1-yPred][i + 1 ] == 'X' ? '*' : '-';` – Saad Nov 02 '16 at 05:40
1

Why not just array[x][y] = 'X'; ?

Also, what are you putting into the rest of the array? If you don't put anything there, it defaults to 0 which displays as nothing on the terminal. I'm guessing you wanted to fill the unused entries with spaces and newlines

joseph
  • 2,429
  • 1
  • 22
  • 43
  • I am reading from the file and then displaying the points to the terminal in the above manner, i.e. the correct output. – Saad Oct 31 '16 at 19:15
  • when i use that, it gives me index out of bounds exception. Also, x-coordinates are in the range [0, 40] and y-coordinates in the range [1, 20].", these are the bounds of my 2d array. – Saad Oct 31 '16 at 19:18
  • The code you have provided doesn't put anything except X's into the character array. The terminal needs spacing and newlines also to properly format the results. Secondly, array[x][y] = 'X'; should be sufficient. There's not need to 'find' the xth row and yth column. You just directly access it since x,y is the location in the array to update. – joseph Oct 31 '16 at 19:19
  • index out of bounds exception indicates you have some points that do not "fit" into your array. you can make the array bigger than 40 by 20 to get around that or rescale your input so it fits into a 40 by 20 array. – joseph Oct 31 '16 at 19:22
  • How would i do the spacing and the formatting? Can you please help me with that? I asked the question cause i have trouble getting the output right which is mainly due to formatting and spacing as you said. – Saad Oct 31 '16 at 19:23
  • I think the index out of bound is due to the fact that x points are from 0 to 40 inclusive and y points are from 1-20 inclusive. If i increase the size of array by one for, won't it work? – Saad Oct 31 '16 at 19:27