Hi, all:

I've just spent a while struggling with a problem that's come up a few times previously, and I'm hoping to get some help from the accomplished monks here. I've got it solved by brute force, and am hoping for a smarter (and perhaps more readable) solution.

I've lost a lot of weight lately, and wanted to plot it on a graph. Unfortunately, I haven't been keeping any kind of a regular track of it - I've been weighing myself whenever it occurs to me - and so I have a very scattered and scant set of data:

6/26/2010 334 8/12/2010 311.8 8/19/2010 308.4 9/5/2010 300.0 9/9/2010 298.6 9/14/2010 297.2 9/16/2010 293.6

Nevertheless, I decided to plot it. Despite my extremely rusty math skills, I figured out that I need to produce a timeline split into some number of intervals, and interpolate the weight at that time based on the two measurements around that point. After struggling with it for a bit, here's what I came up with:

#!/usr/bin/perl -wT use CGI::Carp qw/fatalsToBrowser warningsToBrowser/; use GD::Graph::bars; use POSIX qw/strftime/; use strict; $|++; my @wd; open my $w, "weightdata.txt" or die "weightdata.txt: $!\n"; for (<$w>){ chomp; next unless m{^\s*([\d/]+)\s+([\d.]+)}; my($m, $d, $y) = split /\//, $1; push @wd, [ strftime("%s", 0, 0, 0, $d, $m - 1, $y - 1900), $2 ]; } close $w; my ($tmin, $tmax) = ($wd[0][0], $wd[-1][0]); my ($wmax, $wmin) = ($wd[0][1], $wd[-1][1]); my $interval = ($tmax - $tmin) / 10; my($wcalc, $wgone, @data) = $wmax; for (my $n = $tmin; $n <= $tmax; $n += $interval){ if (defined $wd[1][0]){ shift @wd if $n >= $wd[1][0]; } else { last; } push @{$data[0]}, strftime("%m/%d/%Y", localtime($n)); if ($wd[0][0] == $n){ push @{$data[1]}, $wd[0][1]; push @{$data[2]}, 0; } else { my $tdiff = $wd[1][0] - $wd[0][0]; my $wdiff = $wd[0][1] - $wd[1][1]; $wcalc -= $wdiff / $tdiff * $interval; push @{$data[1]}, sprintf("%.2f", $wcalc); push @{$data[2]}, sprintf("%.2f", $wmax - $wcalc); } } push @{$data[0]}, strftime("%m/%d/%Y", localtime($tmax)); push @{$data[1]}, sprintf("%.2f", $wmin); push @{$data[2]}, sprintf("%.2f", $wmax - $wmin); my $graph = GD::Graph::bars->new(800, 400); $graph->set( x_label => 'Date', y_label => 'Weight changes', title => 'My Recent Weight Loss', y_max_value => 350, show_values => 1, overwrite => 1, long_ticks => 1, bar_spacing => 20, transparent => 0, ); $graph->set_legend("Weight", "Lbs lost"); my $img = $graph->plot(\@data); print "Content-type: image/png\n\n"; print $img->png;

There's got to be a smarter way to interpolate that, though. I started fiddling with figuring out the slope (sine) of each time/weight entry, but got lost. :( In general terms, I'd like to know how to plot data like this, arbitrary positions between unevenly-distributed points.

Thanks for any help you can offer!


--
"Language shapes the way we think, and determines what we can think about."
-- B. L. Whorf

In reply to Interpolating data slope for multiple points by oko1

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.