# Anomalous last digit tally for daily rep

Looking though my daily rep scores with a piece of Java, makes me think that the `ParallelArray` approach might be a good idea, I noticed that the last digits were strangely distributed. Table is my rep positive mod 10.

``````0: 217
1: 15
2: 2
3: 3
4: 55
5: 4
6: 1
7: 137
8: 5
9: 1
``````

Graphical representation (from Open Office) http://blogs.sun.com/tackline/resource/so-rep-red-circles.png

The 0 I can understand. But the 7, 4 and even 1 (-3, -6 and -9), what is that about? Spooky coincidence? Is there some silent discarding of downvotes going on</paranoia>? Some weirdo rounding? Or is it just that my sucky code is buggy?

If I was to guess I'd say that there was some kind of `ceil(daily-rep / 100) * 3` quota on downvotes. I may be seeing things.

My code has been requested. I am ashamed of it.

``````public class Rep {
private static final long[] rep = {

1220601600000L, 54,1220688000000L, 161,1220774400000L,
/* ... bulk of data edited with WordPad's search-and-replace elided ... */
25595,1265961600000L, 25615,1266220800000L, 25642

};
public static void main(String[] args) {
long[] reps = reps(rep);
long prev = 0;
int num = reps.length;

int count = 0;

for (int i=0; i<num; ++i) {
long r = reps[i];
reps[i] -= prev;
if (r - prev >= 200) {
++count;
}

prev = r;
}
System.err.println(count);
java.util.Arrays.sort(reps);
System.err.println(java.util.Arrays.toString(reps));
long[] lastDigits = lastDigits(reps);
java.util.Arrays.sort(lastDigits );
System.err.println(java.util.Arrays.toString(lastDigits ));

int[] d = new int[10];
for (long  x : lastDigits) {
++d[(int)x];
}
System.err.println(java.util.Arrays.toString(d));
for (int i=0; i<10; ++i) {
System.out.println(i+": "+d[i]);
}

}
private static long[] reps(long[] rep) {
int num = rep.length/2;
long[] reps = new long[num];
for (int i=0; i<num; ++i) {
reps[i] = rep[2*i+1];
}
return reps;
}
private static long[] lastDigits(long[] rep) {
int num = rep.length;
long[] lastDigits = new long[num];
for (int i=0; i<num; ++i) {
lastDigits[i] = (1000+rep[i])%10;
}
return lastDigits ;
}
}
``````
• Would you mind sharing the code? – Bill the Lizard Feb 15 '10 at 15:21
• I am ashamed of the code... – Tom Hawtin - tackline Feb 15 '10 at 15:25
• Haha, fair enough. :) – Bill the Lizard Feb 15 '10 at 15:35
• so to make sure i understand: you took net reputation earned/lost on each day that you have been a user, and then looked at only the last digit of those numbers, and counted how many times each digit occurred as the last digit? – Kip Feb 15 '10 at 16:14
• @Kip Yes, that is correct. (Only not for negative values. I took modulo 10, so the six values that were negative were counted as 7 instead of (-)3.) – Tom Hawtin - tackline Feb 15 '10 at 16:18
• @TomHawtin-tackline: Did you do that manually? `System.out.println((-3)%10)` gives `-3`, not `7` – Kip Feb 15 '10 at 16:25
• @Kip The interesting thing is not the numerology, but the value modulo 10 in a mathematical sense. I added 1000 to the number first. I said I was ashamed of the code. – Tom Hawtin - tackline Feb 15 '10 at 18:26
• 0 is obvious, 1 and 4 are anomalous spikes, and all the real work happens inbetween 6-8 weeks, ie, 7. – Adam Davis Feb 15 '10 at 19:45
• Thanks for posting the code. I'm getting the same general shape in my own results, which is strange since I cast on average only about 0.6 downvotes/day. – Bill the Lizard Feb 15 '10 at 20:32
• @Bill Hmm. Could be rounding of some kind. – Tom Hawtin - tackline Feb 15 '10 at 21:03
• @TomHawtin-tackline: I want to try to test my own rep. How did you get the daily rep scores? Did you programmatically fetch and parse the recent activity page for each day, or did you query the data dump somehow? – Kip Feb 17 '10 at 20:38
• @Kip Go to you own user page. Click on the reputation tab. View source. Search for `var d =`. FWIW, your meta results are 0: 156 1: 2 2: 1 3: 1 4: 7 5: 1 6: 0 7: 24 8: 1 9: 1 – Tom Hawtin - tackline Feb 17 '10 at 21:27
• @Tom Hawtin Is that the ROFLCopter in your avatar? – orokusaki Feb 20 '10 at 1:35
• How is this a bug? Are you shamelessly trying to bump your question? – juan Feb 23 '10 at 19:02
• @Downvoter It would appear that as far as the rep graph is concerned (though not the real score), rep that should go to the account that accepted an answer goes to the account that had the answer accepted. How is that possible not a bug? WTF? – Tom Hawtin - tackline Feb 23 '10 at 19:38

## 5 Answers

The data seems to count accepted answers you received as 17 instead of 15.

So without any accepted answers the rep ends in "0", one accepted answer ends in "7" and two accepted answers end in "4". Then follow 1, 8, 5, 2, 9, 6, 3 for an increasingly greater number of accepted answers.

Since days with less accepted answers are more common the daily reputation scores are distributed by the same pattern.

I checked my reputation for a few days back and all discrepancies between the graph numbers and the real rep are explained by this. Additionally, of course, the -1 rep loss for down-votes you give is not reflected in the graph, since down-votes are not public information.

• Hey, I was just coming here to say the same. The original questioner gets +2 from accepting an answer, IIRC. Guess that goes to the wrong place. – Tom Hawtin - tackline Feb 18 '10 at 1:58
• the -1 rep loss is kind of reflected: it goes to the person receiving the downvote, not the person casting it. so the rep graph reflects -3 for received downvotes, +17 for accepted answers. see my answer. in both downvotes and accept "votes", all rep change goes to the person receiving the vote. – Kip Mar 18 '10 at 17:16

According to your profile you've been a StackOverflow member for 528 days and have made 1717 downvotes.

This averages out to just about 3 downvotes per day, or the -3 you're seeing (thus, the trailing digit would be 7).

• +1 for the reason in my answer's edit – balpha Feb 15 '10 at 17:02

Update: I think I've figured it out. I have done some deeper investigation and it looks like the reputation graph makes the following errors when computing daily rep:

• Casting a downvote: -0 (instead of -1)
• Receiving a downvote: -3 (instead of -2)
• Accepting an answer: +0 (instead of +2)
• Getting your answer accepted: +17 (instead of +15)

In both cases, all rep is being awarded to the person receiving the vote, including the rep that is supposed to go to the person casting the vote. For a downvote, the -1 for the voter and -2 for the recipient both go to the recipient. For an accept "vote" both the +2 for the acceptor and the +15 to the acceptee go to the acceptee.

Note that +17 and -3 are equivalent as far as their effect on the last digit is concerned. So each downvote received or answer accepted subtracts 3 from your last digit. This gives the distribution you are seeing: 0, 7, 4, 1, 8, 5, 2, 9, 6, 3 (from most common to least common).

Bounties appear to work correctly, and those can end in 5. The only day that I got rep ending in 2 was when I won a bounty for 175, and took a single downvote (-3).

I ran the numbers for my account and had the same anomaly as you:

```0: 249
1: 6
2: 1
3: 0
4: 10
5: 0
6: 0
7: 39
8: 2
9: 0
```

When I looked at some of the days with 7 as final digit, and looked in my actual reputation tag, the last digit should have been 8. For example, for 2009-08-31, it says I earned -3 rep (which has last "digit" of 7, as defined by OP). But if I go to https://stackoverflow.com/users/recent/18511?Tab=Reputation&StartDate=2009-08-31&EndDate=2009-08-31 (and I realize that this link won't work for anyone else) the only activity I see is one downvote against me.

I also suspected that activity from another day might have spilled over, since the timestamps in the javascript array are all midnight Pacific time (not GMT). So I checked activity from 2009-08-30 through 2010-09-01, and still that one downvote was the only activity. (I think that was Labor Day weekend...)

I wrote my own code to see if it was the same as your results. Here is the code I used:

``````import java.text.*;
import java.util.*;

public final class Main
{

//view reputation graph in user profile, search for "var d = ". Copy&Paste that array here.
private final static String repData = "[[1221811200000, 57],[1222070400000, 77], ... ,[1266134400000, 10817]]";

public static void main(String[] args)
{
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("GMT"));

List<String> dates = new ArrayList<String>();
List<Integer> dailyEarnings = new ArrayList<Integer>();

int lastRep = 0;
boolean isDate = true; //flip this each token, to know if we are at a date or rep
StringTokenizer st = new StringTokenizer(repData, "[], ");
while(st.hasMoreTokens())
{
String token = st.nextToken();
if(token.length() == 0)
continue;

if(isDate)
{
cal.setTimeInMillis(Long.parseLong(token));
dates.add(String.format("%tF", cal));
}
else
{
int rep = Integer.parseInt(token);
dailyEarnings.add(rep - lastRep);
lastRep = rep;
}

isDate = !isDate;
}

int[] tally = new int[10];
for(int i = 0; i < dailyEarnings.size(); i++)
{
System.out.println(dates.get(i) + ": " + dailyEarnings.get(i));

tally[(dailyEarnings.get(i) + 1000)%10]++;
}

for(int i = 0; i < 10; i++)
System.out.println(i + ": " + tally[i]);
}
}
``````
• I'm going to guess that each downvotes received and cast modify reputation as if both a downvote was received (-2) and a downvote was cast (-1). To get the distribution, I'm thinking it might be the existence rather than count of some category of typing (cast vs received, question vs answer). – Tom Hawtin - tackline Feb 17 '10 at 23:47
• If only SO was OS. ;) – Tom Hawtin - tackline Feb 17 '10 at 23:48
• @Tom: I think I've figured it out. Please see my edits. – Kip Feb 18 '10 at 3:33
• Minor detail: a casted downvote not being shown in the graph is by design, I guess, as putting a timestamp on such downvote might reveal to which question it was cast? – Arjan Feb 18 '10 at 9:04
• @ArjanvanBentem: maybe, but the most you could tell is that a user cast a downvote on a given day, you'd have no idea what they downvoted or when during the day they did it. – Kip Feb 22 '10 at 19:56

Edit: The downvoter is not recorded in the database, so my check for your own downvotes was moot. This suggests that John has a point, because your downvotes make the difference.

Original:

I just wrote my own program to check this; I'm posting it here so everyone can look for bugs. Note that this doesn't take the daily rep cap into account (hence the "Total rep" is a little higher than your actual rep), and it doesn't count bounties and days without rep change (both are obviously 0 mod 10).

``````import sqlite3
from collections import defaultdict
import datetime

user_id = 4725

db = sqlite3.connect("so201002.db")
db.row_factory = sqlite3.Row

dayrep = defaultdict(lambda: 0)

for post in db.execute("SELECT * FROM Posts WHERE OwnerUserID = ? AND CommunityOwnedDate IS NULL", (user_id,)):
for downvote_received in db.execute("SELECT * FROM Votes WHERE PostID = ? AND VoteTypeID = 3", (post["ID"],)):
dayrep[downvote_received["CreationDate"]] -= 2
for upvote in db.execute("SELECT * FROM Votes WHERE PostID = ? AND VoteTypeID = 2", (post["ID"],)):
dayrep[upvote["CreationDate"]] += 10
for acceptance in db.execute("SELECT * FROM Votes WHERE PostID = ? AND VoteTypeID = 1 LIMIT 1", (post["ID"],)):
dayrep[acceptance["CreationDate"]] += 15

# no need for this -- the downvoter isn't recorded
# for downvote_cast in db.execute("SELECT * FROM Votes LEFT JOIN Posts ON (Votes.PostID = Posts.ID) WHERE Votes.UserID = ? AND VoteTypeID = 3 AND Posts.CommunityOwnedDate IS NULL", (user_id,)):
#    dayrep[downvote_cast["CreationDate"]] -= 1

print "Total rep: %d" % sum(dayrep.itervalues())

mod10 = [n % 10 for n in dayrep.itervalues()]
for number in xrange(10):
print "%d: %d" % (number, mod10.count(number))
``````

But the result is much closer to what you would expect:

```Total rep: 25499
0: 241
1: 6
2: 2
3: 21
4: 3
5: 97
6: 7
7: 1
8: 50
9: 0```
• How are your results so different from his? – jjnguy Feb 15 '10 at 16:50
• @Justin: See my update. – balpha Feb 15 '10 at 17:01

I believe that the odd numbers are a result of receiving and casting downvotes in a day.

Edit: Assume that a regular day your rep gain will end in 0 baring any 'special cases'.

There are a few 'special cases':

• You cast a downvote (-1)
• You are downvoted (-2)
• You have an answer accepted (+5)

In order for 9 to happen, only one of the specific cases can happen, or a very specific combination of 2 or three of them.

The situation where you gain exactly the rep cap -1 is very rare. That means that you could have only downvoted once, and you have to have an even number of accepted answers before you hit the cap on votes that day. (Otherwise you would end up with a 4)

• But why the distribution. -3 137 times more likely than -1? – Tom Hawtin - tackline Feb 15 '10 at 15:20
• @Tom You have cast lots of downvotes. My guess is that it is just way more likely for you to downvote 3 times in a day rather than just once. – jjnguy Feb 15 '10 at 15:26
• @Justin I don't think you are grasping the concept of 137 times more! – Tom Hawtin - tackline Feb 15 '10 at 15:30
• @Tom I think there is a very reasonable explanation. See my edit. – jjnguy Feb 15 '10 at 15:38
• @Justin I haven't done a Monte Carlo, but I find that unconvincing. For instance, why is -4 so uncommon? And odd number of accepted answers and -3, is also poorly represented. – Tom Hawtin - tackline Feb 15 '10 at 15:41
• @Tom You also have to consider that receiving accepted answers after you have hit the cap for the day are not subject to the cap. 137 times is a very large difference, but I'm not convinced that it is a bug anywhere. – jjnguy Feb 15 '10 at 15:48
• @Tom I think this case is similar to the fact that it is much more likely to roll a 7 than a 12 using 2 six-sided dice. – jjnguy Feb 15 '10 at 15:56
• @Justin Only a mere 36 times more likely. And it's not like rolling a 6 is 36 times less likely. – Tom Hawtin - tackline Feb 15 '10 at 15:59
• @Tom, it would be nice if you could share the code so we could run it against ourselves (or maybe you could). Then we would have more data to discuss. Right now we really don't have enough information. – jjnguy Feb 15 '10 at 16:07
• You forgot accepting answers which gives you a +2. So accepting an answer and having one accepted will give you +7 – ChrisF Feb 15 '10 at 16:45
• @Chris, Tom has only asked 5 questions, so I ruled that case out because it would not have a huge impact on the numbers. – jjnguy Feb 15 '10 at 16:46
• @Justin: Exactly, that's why I didn't even cover them in my check. – balpha Feb 15 '10 at 17:07