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!
In reply to Interpolating data slope for multiple points by oko1
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |