Hello fellow monks,

I was curious about my progress in the XP game, and found it hard to visualize so I made this little script to produce a report on "XP Efficiency."

It attempts to produce a ranking of Saints by comparing XP to either number of posts made, or to how long the Saint has been a Monk.

I made a couple of efforts at making the data more relevant. For example, I exclude anyone who hasn't been active for a certain time, and anyone who has not made a minimum number of posts. This eliminates the Monks who have accumulated thousands of points but are not here in the community these days, as well as those who do come by regularly but have never posted anything.

I also try to get at meaningful data by deducting half a point for each day as a Monk, since I'm most interested in XP efficiency based on real contributions to the Monastery, i.e. XP for upvotes and for votes cast, not for votes accumulated through simple longevity. (Of course it is impossible to properly account for all factors, e.g. the fact that there were far fewer Monks in the early days, so posts had fewer potential upvotes they could earn.) Would like to hear any suggestions on how to improve this "normalization" of the data. Mostly though the results appear to be in line with what I would have expected so I don't think it's too far off. Deducting a point per day seems to make a small difference, and only for XP/posts.

It's just for fun, but it helps motivate me to excel as a teacher and as a student. You can run the script and force it to include you in the rankings even if you don't make the top 20 (or whatever you limit the search to), so you can see your progress at a glance.

Usage:

$ perl XP_efficiency.pl Usage: perl XP_efficiency.pl --sort_by (age|posts) Options: --sort_by, -s: Rank monks by XP/posts or by XP/age --limit, -l: Limit results to this many --order, -o: Sort order (asc|desc) --recent, -r: Limit to monks seen in the last n wee +ks --only_include, -i: Skip all monks except this/these one( +s) --force_include, -f: Include this/these monk(s) even if -l + is set --min_posts, -m: Exclude monks with fewer than this ma +ny posts --no_adjust, -n: Don't deduct 0.5 XP for each day of m +onkhood --help, -h: Print this help

A couple of example reports:

$ perl XP_efficiency.pl --sort_by age --min_posts 200 --recent 3 --lim +it 10 --order desc +--------------------------------------------------------------------- +-----------------+ | Pos | St. | Monk name | XP | Level | Age | XP/Age | +Posts | XP/Posts | +--------------------------------------------------------------------- +-----------------+ | 1 2 BrowserUk 159,881 Pope (28) 4,971 36.0644 +22,404 6.9144 | | 2 3 ikegami 127,624 Pope (28) 4,173 34.2347 +19,716 6.2615 | | 3 4 Corion 122,789 Pope (28) 5,783 23.4148 + 9,796 11.9443 | | 4 5 GrandFather 72,446 Sage (25) 3,880 20.4526 + 6,562 10.4489 | | 5 55 Athanasius 24,854 Canon (20) 1,352 20.1077 + 1,458 16.1193 | | 6 664 1nickt 3,115 Curate (13) 214 15.6199 + 468 6.1987 | | 7 42 choroba 29,288 Canon (20) 2,112 14.8885 + 3,739 7.2683 | | 8 20 toolic 43,023 Bishop (22) 3,130 14.7489 + 3,597 11.0906 | | 9 7 tye 70,849 Sage (25) 5,661 13.3272 + 7,887 8.2652 | | 10 60 kcott 24,138 Canon (20) 1,939 13.2484 + 2,407 9.2227 | +--------------------------------------------------------------------- +-----------------+
The next two show the effect of adjusting to discount the daily freebie XP point (not really seen when sorting by XP/age; this is XP/posts):
$ perl XP_efficiency.pl -s posts -m 200 -l 5 -o desc -n +--------------------------------------------------------------------- +-----------------+ | Pos | St. | Monk name | XP | Level | Age | XP/Age | +Posts | XP/Posts | +--------------------------------------------------------------------- +-----------------+ | 1 82 gryphon 18,249 Abbot (19) 5,643 3.7429 + 218 83.7110 | | 2 17 Old_Gray_Bear 46,694 Bishop (22) 4,507 11.9908 + 598 78.0836 | | 3 72 gmax 20,739 Abbot (19) 5,164 4.6478 + 292 71.0240 | | 4 54 Perlbotics 24,920 Canon (20) 3,056 9.4366 + 384 64.8958 | | 5 34 Gavin 32,148 Chancellor (21) 3,599 10.3364 + 600 53.5800 | +--------------------------------------------------------------------- +-----------------+
$ perl XP_efficiency.pl -s posts -m 200 -l 5 -o desc +--------------------------------------------------------------------- +-----------------+ | Pos | St. | Monk name | XP | Level | Age | XP/Age | +Posts | XP/Posts | +--------------------------------------------------------------------- +-----------------+ | 1 17 Old_Gray_Bear 46,694 Bishop (22) 4,507 10.8334 + 598 70.5468 | | 2 82 gryphon 18,249 Abbot (19) 5,643 2.5855 + 218 57.8257 | | 3 54 Perlbotics 24,920 Canon (20) 3,056 8.2794 + 384 56.9375 | | 4 72 gmax 20,739 Abbot (19) 5,164 3.4905 + 292 53.3390 | | 5 34 Gavin 32,148 Chancellor (21) 3,599 9.1792 + 600 47.5817 | +--------------------------------------------------------------------- +-----------------+
Include yourself and someone else and see how you stack up:
$ perl XP_efficiency.pl -s posts -m 200 -l 10 -o desc -f 1nickt -f mer +lyn +--------------------------------------------------------------------- +-----------------+ | Pos | St. | Monk name | XP | Level | Age | XP/Age | +Posts | XP/Posts | +--------------------------------------------------------------------- +-----------------+ | 1 17 Old_Gray_Bear 46,694 Bishop (22) 4,507 10.8334 + 598 70.5468 | | 2 82 gryphon 18,249 Abbot (19) 5,643 2.5855 + 218 57.8257 | | 3 54 Perlbotics 24,920 Canon (20) 3,056 8.2794 + 384 56.9375 | | 4 72 gmax 20,739 Abbot (19) 5,164 3.4905 + 292 53.3390 | | 5 34 Gavin 32,148 Chancellor (21) 3,599 9.1792 + 600 47.5817 | | 6 100 scorpio17 16,117 Abbot (19) 3,253 4.5761 + 323 39.8266 | | 7 61 hsmyers 23,992 Canon (20) 5,452 3.9352 + 520 35.6538 | | 8 44 borisz 27,915 Canon (20) 4,756 5.6351 + 913 25.3658 | | 9 129 dvergin 12,732 Monsignor (18) 5,825 1.3724 + 311 22.2090 | | 10 58 FunkyMonk 24,201 Canon (20) 3,187 7.6302 + 1,024 20.5215 | | 41 6 merlyn 71,157 Sage (25) 5,741 13.1874 + 6,322 10.3474 | | 82 664 1nickt 3,115 Curate (13) 214 15.6198 + 468 6.1987 | +--------------------------------------------------------------------- +-----------------+

Code if you would like to play with it:

#!/usr/bin/perl use strict; use warnings; use feature qw/ say /; use Data::Dumper; $Data::Dumper::Sortkeys = 1; use Getopt::Long; use HTTP::Tiny; use HTML::TableExtract; use Time::Piece; use Number::Format qw/ format_number /; use List::Util qw/ any /; my ( $sort_by, $order, $limit, $recent, $only_include, $force_include, + $min_posts, $no_adjust ); GetOptions( 'sort_by|s=s' => \$sort_by, 'order|o=s' => \$order, 'limit|l=i' => \$limit, 'recent|r=i' => \$recent, 'only_include|i=s@' => \$only_include, 'force_include|f=s@' => \$force_include, 'min_posts|m=i', => \$min_posts, 'no_adjust|n' => \$no_adjust, 'help|?' => \&print_usage, ); minor_validation(); my $url = 'http://perlmonks.com/?node_id=3559'; my $headers = ['#', 'User', 'Experience', 'Level', 'Writeups', 'User S +ince', 'Last Here']; my $data; fetch_data( $url ); print_data( $data ); exit; ###################################### sub fetch_data { my $url = shift; my $page = HTTP::Tiny->new->get( $url ); die 'Died. No response' if not $page; die "Died. $page->{'status'} $page->{'reason'}" if not $page->{'su +ccess'}; my $table = HTML::TableExtract->new( 'headers' => $headers ); $table->parse( $page->{'content'} ); my $now = time(); foreach my $row ( $table->rows ) { process_row( $now, $row ); } } sub process_row { my $now = shift; my $row = shift; my ( $rank, $monk, $xp, $level, $posts, $created, $last_here ) = @ +{ $row }; return if $last_here =~ /year/ or ( $last_here =~ /(\d+) week/ and + $1 > ( $recent || 4 ) ); return if $sort_by eq 'posts' and $posts eq 'None'; return if $min_posts and ( $posts eq 'None' or $posts < $min_posts + ); return if $only_include and not any { $_ eq $monk } @{ $only_inclu +de }; $created = Time::Piece->strptime($created, '%Y-%m-%d %R')->epoch; my $age = $now - $created; my $age_days = int( $age / 86400 ); my $xp_adjusted = $no_adjust ? $xp : $xp - ( $age_days / 2 ); my %record = ( rank => $rank, xp => $xp, level => $level, posts => $posts, by_posts => $posts eq 'None' ? 0 : ( $xp_adjusted / $posts ), age => $age_days, by_age => ( $xp_adjusted / $age ) * 100000, # for scale ); $data->{ $monk } = \%record; } sub print_data { my $data = shift; $sort_by = 'by_' . $sort_by; my %sortable = ( map { $_ => $data->{ $_ }->{ $sort_by } } keys %{ + $data } ); my @results; foreach my $monk ( sort { $sortable{$a} <=> $sortable{$b} } keys % +sortable ) { $data->{ $monk }->{'posts'} =~ s/None/0/; push @results, { rank => $data->{ $monk }->{'rank'}, monk => $monk, xp => format_number( $data->{ $monk }->{'xp'} ), level => $data->{ $monk }->{'level'}, age => format_number( $data->{ $monk }->{'age'} ), by_age => format_number( $data->{ $monk }->{'by_age'}, 4 +, 1 ), posts => format_number( $data->{ $monk }->{'posts'} ), by_posts => format_number( $data->{ $monk }->{'by_posts'}, + 4, 1 ) }; } @results = reverse @results if $order and $order eq 'desc'; print <<EOT; +--------------------------------------------------------------------- +-----------------+ | Pos | St. | Monk name | XP | Level | Age | XP/Age | +Posts | XP/Posts | +--------------------------------------------------------------------- +-----------------+ EOT my $count = 0; foreach my $result ( @results ) { ++$count; if ( $limit and $count > $limit ) { last if not defined $force_include; next unless any { $_ eq $result->{'monk'} } @{ $force_incl +ude }; } format = @ @### @#### @<<<<<<<<<<<< @>>>>>> @>>>>>>>>>>>>>>> @>>>>>> @>>>>>>> @ +>>>>>> @>>>>>>>> @ '|', $count, $result->{'rank'}, $result->{'monk'}, $result->{'xp'}, $r +esult->{'level'}, $result->{'age'}, $result->{'by_age'}, $result->{'p +osts'}, $result->{'by_posts'}, '|' . write; } print '+---------------------------------------------------------- +----------------------------+'; } sub minor_validation { print_usage() if ( ( ! $sort_by or $sort_by !~ /age|posts/ ) or ( $order and $order !~ /asc|desc/ ) ); } sub print_usage { say <<"EOT"; Usage: perl $0 --sort_by (age|posts) Options: --sort_by, -s: Rank monks by XP/posts or by XP/age --limit, -l: Limit results to this many --order, -o: Sort order (asc|desc) --recent, -r: Limit to monks seen in the last n wee +ks --only_include, -i: Skip all monks except this/these one( +s) --force_include, -f: Include this/these monk(s) even if -l + is set --min_posts, -m: Exclude monks with fewer than this ma +ny posts --no_adjust, -n: Don't deduct 0.5 XP for each day of m +onkhood --help, -h: Print this help EOT exit; } __END__

edit: reduce XP by only 0.5 per day; switched to Time::Piece as DateTime::Format::Strptime has compile problems on Strawberry Perl; switched to format_number() because round() does not provide trailing zeroes; increase XP/Age by two more orders of magnitude, for scale


The way forward always starts with a minimal test.

In reply to Ranking the Saints by XP Efficiency by 1nickt

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.