add a line according to an equation? y=mx+b?

muppet77
2012-02-27
2012-09-07
1 2 > >> (Page 1 of 2)
  • muppet77
    muppet77
    2012-02-27

    i already have a graph beautifully drawn out at http://www.maidenerleghweathe
    r.com/cetchangegraph.php

    i wish to add a simple straight line according to the equation y=mx+b

    the values of m and b are found in another file called regression.php and are
    known as $m and $b.

    as you can see i already have two lines drawn, (red and black) but NOT my
    y=mx+b line yet.

    how do i do it?

    i have left my phplot code alone and added the below code, hoping that it
    would superimpose the line????

    require ("regression.php");
    $m = $result['m'];
    $b = $result['b'];
    
    $data = array();
    for ($x = 1; $x <= 20; $x++) $data[] = array('1,2,3,4,5,6,7,8,9,10', ($m * $x)+$b);
    
    $legend_text = array($next, $thism, $text3);
    $plot = new PHPlot(800, 600);
    $plot->SetTitle($thism." (Shawbury +0.5C as proxy)");
    $plot->SetDataType('data-data');
    $plot->SetDataValues(read_prices_text_data(DATAFILE));
    $plot->SetPlotType('linepoints');
    $plot->SetDataColors(array('green'));
    $plot->SetLegend($legend_text);
    $plot->SetLegendWorld(0, 20);
    $plot->SetXLabelAngle(90);
    $plot->SetXTickPos('none');
    $plot->SetLineStyles('solid');
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('plotin');
    $plot->SetDrawBrokenLines(True);
    $plot->DrawGraph();
    
     
  • muppet77
    muppet77
    2012-02-27

    in fact I have removed

    $m = $result;

    $b = $result;

    as i don't need it.

    $m and $b are fine on their own as that's what they are called in
    regression.php

     
  • muppet77
    muppet77
    2012-02-27

    one issue is that my original graph has dates on the x axis and i think i need
    numbers for my y=mx+b.

    my original has $plot->SetDataType('text-data');

    but my next version has

    $plot->SetDataType('data-data');

    not sure what i should be doing so i'll stop now and see if anyone can help!

    thanks!

     
  • lbayuk
    lbayuk
    2012-02-27

    There are a couple of questions, and it might be better to deal with them one
    at a time.

    The last, easier one first: If your data array has any non-empty strings in
    the label positions (0 index of each row), then PHPlot will by default give
    them priority over tick labels, assuming that if you put them in the array
    they must be important to you. So you will see the label strings, not the X
    values (tick labels) along the axis. If you want to see the tick labels
    instead of data labels, either (1) don't put the labels in the array - leave
    them all empty, or (2) use $plot->SetXTickLabelPos('plotdown') to position the
    tick labels, or (3) use $plot->SetXDataLabelPos('none') to un-position the
    data labels.

    Be aware that the tick labels probably won't line up under your data points,
    the way the data labels do. Tick labels and tick marks (which you turned off)
    are at fixed intervals along the bottom. There are PHPlot functions to control
    them.

     
  • muppet77
    muppet77
    2012-02-27

    ok I am not fussed about data points on the line so will alter it.

    can I change it on the original as I'd like the red and black lines to have
    them?

    what next?

     
  • muppet77
    muppet77
    2012-02-27

    ok gone for your option (3).

    what now?

    just to be clear my entire code is as follows (first graph plots red and black
    lines fine, second graph is my own effort.

    i am trying to get a straight line drawn y=mx+b : will it superimpose on the
    first, the way i am doing it?

    <?php
    date_default_timezone_set('Europe/London'); 
    ////////////////////////////////////////////////////////////////////////////
    # PHPlot Example: OHLC (Financial) plot, basic lines, using
    # external data file, text-data format.
    $namefile = "GFS/".date("F")."cetchange.txt";
    define('DATAFILE', $namefile); // External data file
    require_once 'phplot.php';
    function read_prices_text_data($filename)
    {
        $f = fopen($filename, 'r');
        if (!$f) {
            fwrite(STDERR, "Failed to open data file: $filename\n");
            return FALSE;
        }
        // Read and check the file header.
        $row = fgetcsv($f);
        if ($row === FALSE || $row[0] != 'Date' || $row[1] != 'cet'
                || $row[2] != 'predict'  || $row[3] != '16d') {
            fwrite(STDERR, "Incorrect header in: $filename\n");
            return FALSE;
        }
        // Read the rest of the file into array keyed by date for sorting.
        while ($r = fgetcsv($f)) {
            $d[$r[0]] = array( $r[1], $r[2], $r[3]);
        }
        fclose($f);
     //   ksort($d);
        // Convert to a PHPlot data array with label and 3 values per row.
        foreach ($d as $date => $r) {
            $data[] = array($date, $r[0],$r[1], $r[2]);
        }
        return $data;
    }
    
    $thism = date("F")." CET prediction";
    $next = "HadCET for ".date("M")." so far";
    $day16 = date("j M",mktime(0, 0, 0, date("m")  , date("d")+16, date("Y")));
    $text3 = "1 ".date("M"). " to ".$day16." CET prediction";
    
    $legend_text = array($next, $thism, $text3);
    $plot = new PHPlot(800, 600);
    $plot->SetTitle($thism." (Shawbury +0.5C as proxy)");
    $plot->SetDataType('text-data');
    $plot->SetDataValues(read_prices_text_data(DATAFILE));
    $plot->SetPlotType('linepoints');
    $plot->SetDataColors(array('black', 'red', 'blue'));
    $plot->SetLegend($legend_text);
    $plot->SetLegendWorld(0, 20);
    $plot->SetXLabelAngle(90);
    $plot->SetXTickPos('none');
    //$plot->SetPlotAreaWorld(NULL, 0, NULL, 20);
    $plot->SetLineStyles('solid');
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('plotin');
    $plot->SetDrawBrokenLines(True);
    
    $plot->DrawGraph();
    //////////////////////////////////////////////////////////////////////////
    
    require ("regression.php");
    
    $data = array();
    for ($x = 1; $x <= 20; $x++) $data[] = array('1,2,3,4,5,6,7,8,9,10', ($m * $x)+$b);
    
    $legend_text = array($next, $thism, $text3);
    $plot = new PHPlot(800, 600);
    $plot->SetTitle($thism." (Shawbury +0.5C as proxy)");
    $plot->SetDataType('data-data');
    $plot->SetDataValues(read_prices_text_data(DATAFILE));
    $plot->SetPlotType('linepoints');
    $plot->SetDataColors(array('green'));
    $plot->SetLegend($legend_text);
    $plot->SetLegendWorld(0, 20);
    $plot->SetXLabelAngle(90);
    $plot->SetXTickPos('none');
    $plot->SetXDataLabelPos('none');
    $plot->SetLineStyles('solid');
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('plotin');
    $plot->SetDrawBrokenLines(True);
    $plot->DrawGraph();
    
     
  • lbayuk
    lbayuk
    2012-02-27

    No it won't work that way. The first DrawGraph() sends the image, and the rest
    is probably ignored by the viewer/browser. Read the below (which I wrote
    before reading your latest).

    I can think of 3 ways to add a line to your plot. You can consider it to be an
    additional data set, an overlay plot, or an annotation.

    Using an additional data set would involve adding a column to your data array,
    probably with just 2 points (1 at Xmin, 1 at Xmax). I don't think is practical
    in this case, because it involves messing with every row in the data array.

    With an overlay plot, you would use $plot->SetPrintImage(False), do your main
    graph, $plot->DrawGraph(), then make a new data array with just one line,
    $plot->DrawGraph() again, and $plot->PrintImage() to output it. The second
    graph will overlay the first. (Similar to what you posted, but you must turn
    off automatic image print or you get 2 concatenated PNG images which won't
    work.)

    The third way is to use a drawing callback function to annotate your plot.
    This is the most flexible way, but can take a little more work. There is an
    example in the manual, but if you want I can post a little code that is more
    specific to what you want. Or if you want to try the overlay, I can show you
    one of those.

     
  • muppet77
    muppet77
    2012-02-27

    posting overlay plot code...

     
  • muppet77
    muppet77
    2012-02-27

    $plot->SetPrintImage(False);

    $legend_text = array($next, $thism, $text3);
    $plot = new PHPlot(800, 600);
    $plot->SetTitle($thism." (Shawbury +0.5C as proxy)");
    $plot->SetDataType('text-data');
    $plot->SetDataValues(read_prices_text_data(DATAFILE));
    $plot->SetPlotType('linepoints');
    $plot->SetDataColors(array('black', 'red', 'blue'));
    $plot->SetLegend($legend_text);
    $plot->SetLegendWorld(0, 20);
    $plot->SetXLabelAngle(90);
    $plot->SetXTickPos('none');
    $plot->SetLineStyles('solid');
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('plotin');
    $plot->SetDrawBrokenLines(True);
    
    $plot->DrawGraph();
    
    require ("regression.php");
    
    $data = array();
    for ($x = 1; $x <= 200; $x++) $data[] = array('1,2,3,4,5,6,7,8,9,10', ($m * $x)+$b);
    
    $plot->DrawGraph();
    
    $plot->PrintImage();
    

    returns: Fatal error: Call to a member function SetPrintImage() on a non-
    object on this line

    $plot->SetPrintImage(False);

     
  • muppet77
    muppet77
    2012-02-27

    when i // out the $plot->SetPrintImage(False);

    it draws the graph again but without the extra new data line.

    ?

     
  • lbayuk
    lbayuk
    2012-02-28

    1. Call to a member function on non-object: You have to create the object before you can use it. Move that $plot->SetPrintImage(False) line anywhere after $plot=new PHPlot(...) and before the first $plot->DrawGraph().

    2. You need to use $plot->SetDataValues($data) to load the data array for your second plot (the overlay).

    3. Set the data colors again before the overlay. The first color slot (not the 3rd) will be used in the overlay.

    4. The contents of the data array for the second plot could just be two points. You need to know Xmin and Xmax of your data, and calculate Y1 and Y2 from $m*$x+$b, giving you something like:

      $data = array(array('', $xmin, $m$xmin+$b), array('', $xmax, $m$xmax+$b));

    Except that won't work with text-data data type, which has implicit X values.
    OK, that makes it harder. I'm going to have to try this out first. Are you
    sure you don't want to try the annotation method using a callback?

     
  • muppet77
    muppet77
    2012-02-28

    ok - an easier idea.

    in another php script (regression.php) i can calculate all the values needed.
    if so, would you help me to draw the line please?

    i will call them:

    $xmin (the first date in the sequence)

    $xmax (the last date in the sequence)

    $ymin (derived from y=mx+b)

    $ymax (as above)

    i will be able to count the rows of data which will help me work out $xmin and
    $xmax.

    how does that sound? so i will have those 4 variables ready to use in the
    script?

    thanks in advance.

     
  • muppet77
    muppet77
    2012-02-28

    ok, the variables held in regression.php are

    $xmin = Wed 02/15 @ 12Z

    $xmax = Tue 02/28 @ 06Z

    $ymin = 3.7

    $ymax = 4.3

    please could you help me integrate these to draw a line between these
    coordinates?

    how do i alter my code?

    thanks

     
  • muppet77
    muppet77
    2012-02-28

    and this is the code i wrote - no luck though :-(

    $plot->SetPrintImage(False); // Defer output until the end
    
    $legend_text = array($next, $thism, $text3);
    $plot = new PHPlot(800, 600);
    $plot->SetTitle($thism." (Shawbury +0.5C as proxy)");
    $plot->SetDataType('text-data');
    $plot->SetDataValues(read_prices_text_data(DATAFILE));
    $plot->SetPlotType('linepoints');
    $plot->SetDataColors(array('black', 'red', 'blue', 'yellow'));
    $plot->SetLegend($legend_text);
    $plot->SetLegendWorld(0, 20);
    $plot->SetXLabelAngle(90);
    $plot->SetXTickPos('none');
    $plot->SetLineStyles('solid');
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('plotin');
    $plot->SetDrawBrokenLines(True);
    $plot->DrawGraph();
    
    require ("regression.php");
    $data2 = array(
    array('$xmin', $ymin),
    array('$xmax', $ymax)
    );
    
    $legend_text = array($next, $thism, $text3);
    $plot = new PHPlot(800, 600);
    $plot->SetTitle($thism." (Shawbury +0.5C as proxy)");
    $plot->SetDataType('text-data');
    $plot->SetDataValues($data2);
    $plot->SetPlotType('linepoints');
    $plot->SetDataColors(array('yellow'));
    $plot->SetLegend($legend_text);
    $plot->SetLegendWorld(0, 20);
    $plot->SetXLabelAngle(90);
    $plot->SetXTickPos('none');
    $plot->SetLineStyles('solid');
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('plotin');
    $plot->SetDrawBrokenLines(True);
    
    $plot->DrawGraph();
    $plot->PrintImage();
    
     
  • muppet77
    muppet77
    2012-02-28

    i have tried teh xmin and xmax with and without quotes. they are meant to be
    dates, so i wasn't sure?

     $data2 = array(
    array('$xmin', $ymin),
    array('$xmax', $ymax)
    );
    
     
  • muppet77
    muppet77
    2012-02-28

    RIGHT, MOVEMENT!

    I moved the position of setprintimagefalse and now i get the SECOND NEW PLOT
    ONLY. My original lines have gone. Looks like only the second one is working?

    i now have

    $legend_text = array($next, $thism, $text3);
    $plot = new PHPlot(800, 600);
    $plot->SetPrintImage(False); // Defer output until the end
    
    $plot->SetTitle($thism." (Shawbury +0.5C as proxy)");
    $plot->SetDataType('text-data');
    $plot->SetDataValues(read_prices_text_data(DATAFILE));
    $plot->SetPlotType('linepoints');
    $plot->SetDataColors(array('black', 'red', 'blue'));
    $plot->SetLegend($legend_text);
    $plot->SetLegendWorld(0, 20);
    $plot->SetXLabelAngle(90);
    $plot->SetXTickPos('none');
    $plot->SetLineStyles('solid');
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('plotin');
    $plot->SetDrawBrokenLines(True);
    $plot->DrawGraph();
    
    require ("regression.php");
    $data2 = array(
    array($xmin, $ymin),
    array($xmax, $ymax)
    );
    
    $legend_text = array($next, $thism, $text3);
    $plot = new PHPlot(800, 600);
    $plot->SetTitle($thism." (Shawbury +0.5C as proxy)");
    $plot->SetDataType('text-data');
    $plot->SetDataValues($data2);
    $plot->SetPlotType('linepoints');
    $plot->SetDataColors(array('yellow'));
    $plot->SetLegend($legend_text);
    $plot->SetLegendWorld(0, 20);
    $plot->SetXLabelAngle(90);
    $plot->SetXTickPos('none');
    $plot->SetLineStyles('solid');
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('plotin');
    $plot->SetDrawBrokenLines(True);
    
    $plot->DrawGraph();
    $plot->PrintImage();
    
     
  • muppet77
    muppet77
    2012-02-28

    ...and the good news is that the data is getting sucked from regression.php
    ok.

    but hwy no first plot overlaying the second?

    any ideas?

    thanks

     
  • muppet77
    muppet77
    2012-02-28

    could it be down to the fact that my x values are realy text values and so it
    is not matching them up?

     
  • lbayuk
    lbayuk
    2012-02-28

    Two problems I see right away with post #16 above is that your 2nd data array
    is not correct, and you are creating a 2nd PHPlot object - which does not
    overlay the plots, as you discovered. Sorry this is taking longer than it
    should - partly because I couldn't duplicate your script because of the
    external function you use to get the data (and partly because we seem to be on
    different time zones...)

    Let me make up some fake data and then I can do a complete script, and we can
    work from there.

     
  • lbayuk
    lbayuk
    2012-02-28

    OK, I got a plot with fake data and a yellow line across, but there is a
    problem with "y = mx + b" not being well defined because X is not defined in
    your first data set. That is, you are leaving "x" up to PHPlot by using text-
    data, but then you want to draw a line y=mx+b for given m and b over it. Now,
    I know you know what you want to do here, but can you explain it? For example,
    do you want your first data point (Wed 2/15 12Z in the sample graph you
    posted) to correspond to X=0? What about the other end - is there a fixed
    number of points (looks like 49 or 50 in the sample), and what X would that
    be?

     
  • muppet77
    muppet77
    2012-02-28

    the number of x dates changes the whole time.

    I call the first date x =1

    I can give you a variable to let you know what the last x is if you need it
    rather than working with dates?

     
  • muppet77
    muppet77
    2012-02-28

    and by that I mean I have a variable that is 52 or however many dates there
    are.

    assume it is called $datecount if that helps coding it.

     
  • muppet77
    muppet77
    2012-02-28

    if you use my y min and y max variables then yes, x=1 and x=$datecount every
    time.

     
  • lbayuk
    lbayuk
    2012-02-28

    OK, here we go. This should give you something to work from. First, a summary
    of what I changed:

    1. The part at the top between "Fake data" and "End fake data" is added to build a data array, provide values for unknown variables, etc. You shouldn't need any of this (but try it this way first).

    2. I removed SetLegendWorld() so PHPlot will place the legend in the corner. Your position was off the plot (Y=20 is not within range) so you won't get a legend. Rather than fix it, I just left it out so you get the default position.

    3. For the second plot, I made a data array with 2 points, using data-data format (so I can specify explicit X values). If I did this right, this will draw Y = m * X + b on your graph, with X=1 being the first data point, and X=$datecount being the last.

    4. I removed redundant code in your second plot. You don't want title, etc. Legend and Y data labels need to be explicitly turned off, or they get drawn twice. Also I made this a lines plot, not linespoints, because you don't need the points here. As I said in (3), data type is data-data here.

    Try it first, before plugging in your functions. (If you have trouble copying
    code from Sourceforge's forums, sorry, they know about it but aren't going to
    fix it - affects some browsers only. If you are getting this thread by email
    too, try the email version instead.)

    <?php
    # forum: 2/28/2012 muppet77
    require_once 'phplot.php';
    
    # ==== Fake data (some from sample image):
    $next = "Legend line 1";
    $thism = "February CET Prediction";
    $text3 = "Legend line 3";
    $datecount = 52; // Number of date points
    $m = 0.06; // Slope of added line, Y = m * X + b
    $b = 0.94; // Intercept of added line
    
    define('DATAFILE', 'unused');
    function read_prices_text_data($ignored)
    {
        global $datecount;
        $data = array();
        mt_srand(0);
        $base = gmmktime(12, 0, 0, 2, 15);
        for ($i = 0; $i < $datecount; $i++) {
            # Label is date as: "Mon 02/15 @ 00Z"
            $data[] = array(gmstrftime('%a %m/%d @ %HZ', $base),
                            mt_rand(5, 38) / 10,
                            mt_rand(33, 44) / 10);
            $base += 6 * 60 * 60; // 6 hours
        }
        return $data;
    }
    
    # ==== End fake data
    
    $legend_text = array($next, $thism, $text3);
    $plot = new PHPlot(800, 600);
    $plot->SetPrintImage(False); // Defer output until the end
    
    $plot->SetTitle($thism." (Shawbury +0.5C as proxy)");
    $plot->SetDataType('text-data');
    $plot->SetDataValues(read_prices_text_data(DATAFILE));
    $plot->SetPlotType('linepoints');
    $plot->SetDataColors(array('black', 'red', 'blue'));
    $plot->SetLegend($legend_text);
    // Note this won't work, Y=20 is off the graph
    //$plot->SetLegendWorld(0, 20);
    $plot->SetXLabelAngle(90);
    $plot->SetXTickPos('none');
    $plot->SetLineStyles('solid');
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('plotin');
    $plot->SetDrawBrokenLines(True);
    $plot->DrawGraph();
    
    // Code below replaces this:
    //require ("regression.php");
    
    /* Mapping date values to X value:
       First date is x=1 (PHPlot uses x=0.5)
       Last date is x=$datecount (PHPlot uses $datecount-0.5).
       The line to add is Y = m * X + b for constants m, b.
    
       Create a data array with 2 points that solve that equation, but using
       PHPlot's idea of the X range.
           Y(x=1) = m + b
           Y(x=datecount) = m * datecount + b
       See top of file for $m and $b
    */
    
    $data2 = array(
      array('', 0.5,  $m + $b),
      array('', $datecount - 0.5, $m * $datecount + $b),
    );
    
    $plot->SetDataType('data-data'); // Note: data-data, array has X values
    $plot->SetDataValues($data2);
    $plot->SetPlotType('lines');
    $plot->SetDataColors(array('yellow'));
    $plot->SetLineWidths(2);
    $plot->SetYDataLabelPos('none'); // Need to turn these off
    $plot->SetLegend(NULL); // Cancel legend
    
    $plot->DrawGraph();
    $plot->PrintImage();
    
     
1 2 > >> (Page 1 of 2)