-19

I am interested in analyzing directed voting behavior of users in relation to their reputation on stackoverflow. Since voting is an anonymous activity for obvious reasons, I was hoping that I could get, in addition to voting data, the relative userID information (to preserve anonymity) for the voter and voted along with their reputations. My aim is to develop a who-votes-for-who network of signed nodes based on the difference in users' reputations.

If anyone knows if this can be achieved with the current data, please let me know! Otherwise, I hope this request can be taken into consideration.

Thanks a lot in advance!

11

I understand how you might want to use this information to gain still anonymous information: if people vote primarily in one area etc.

But it would be ridiculously easy to reverse engineer this to reveal the actual data based on just a few bits of data for example

You're much more likely to vote on a question you're publicly involved it.

  • A single question will give n public interactors <--> m secret voters where n and m are small.
  • A second question will give another n public interactors <--> m secret voters where n and m are small.

It is likely there will be exactly 1 n <--> m pair that is common between the two data sets. Once that happens you can be confident that that n <--> m pair is true and you've exposed what should be secret. The more unique pairs you can find the higher your confidence level.

Using just this I expect you could break the publicID <--> secretVoterID relationship for anyone with more than a glancing interaction with the site.

For this reason I would be against releasing full voting histories, even with an anonymised UserID

Unscientific test

From my own voting data if I vote on a question I publicly interact with it about 50% of the time. I'm going to assume I'm typical (with no data to the contrary). My total interacts (comments + answers + questions) is also roughly equal to my total number of votes.

Because I had a spare 5 minutes and was interested I've written a quick simulation to simulate the following:

500 questions
10 interactions on a question
10 votes on a question
Average 10 interacts per user
Average 10 votes per user
50% chance a user who interacts on a question also votes on that question
500 users (is calculated from the above data)

I then attempted to use the above "maximum pairs" technique to break the secret user ID. I got the following data

Correct guesses: 442
Wrong guesses: 58

So 90% correct. The larger the number of questions the easier it got. This is not exactly scientific and is full of assumtions. But it shows how easy it would be to extract the data.

Test program (written in java)

public class SecretIDDetection {

    static Collection<Question> questions=new HashSet<>();
    static ArrayList<User> users=new ArrayList<>();

    //averages

    static double voteOnInteractChance=0.5;
    static int interactionsPerQuestion=10;
    static int votesPerQuestion=10;
    static int votesPerUser=10;
    static int noOfQuestions=500;
    static int noOfUsers=(noOfQuestions*votesPerQuestion)/votesPerUser;

    public static void main(String[] args) {

        seedData();
        attemptToBreakData();
    }

    public static void seedData(){
        Random rnd=new Random();
        for(int i=0;i<noOfUsers;i++){
            users.add(new User(i, rnd.nextInt()));
        }

        for(int i=0;i<noOfQuestions;i++){
            Question q=new Question();
            int votesSoFar=0;
            for(int j=0;j<interactionsPerQuestion;j++){
                //choose a random user to interact

                User u=users.get(rnd.nextInt( users.size()));
                q.addInteractors(u);
                if (rnd.nextDouble()<voteOnInteractChance){
                    //also vote
                    q.addVoter(u);
                    votesSoFar++;
                }


            }

            //add some random votes to make up the numbers
            for(int j= votesSoFar;j<votesPerQuestion;j++){
                  q.addVoter(users.get(rnd.nextInt( users.size())));
            }

            questions.add(q);

        }
    }


    public static void attemptToBreakData(){
        for(Question q:questions){
            q.addSuspicions();
        }

        int correct=0;
        int wrong=0;
        for(User u:users){
            u.printIdentityData();
            if (u.guessedCorrectly()){
                correct++;
            }else{
                wrong++;
            }
        }

        System.out.println("Correct guesses: " + correct);
        System.out.println("Wrong guesses: " + wrong);


    }
}
public class Question {
    Collection<Integer> voters=new HashSet<>();
    Collection<User> interactors=new HashSet<>();

    public void addVoter(User voter){
        voters.add(voter.secretID);
        voter.addVote();
    }
    public void addInteractors(User interactor){
        interactors.add(interactor);
        interactor.addInteract();
    }

    public void addSuspicions(){
        for(User interactor: interactors){
            for(Integer integer:voters){
                interactor.addSuspicion(integer);
            }
        }
    }
}
public class User {
    public int publicID;
    public int secretID;

    private int totalVotes=0;
    private int totalInteracts=0;

    public  HashMap<Integer, Integer> suspicions= new HashMap<>();;

    public User(int publicID, int secretID) {
        this.publicID = publicID;
        this.secretID = secretID;
    }

    void addSuspicion(Integer integer) {
        Integer currectValue=suspicions.get(integer);
        Integer newValue=(currectValue==null?1:currectValue+1);

        suspicions.put(integer,newValue);
    }

    private int getMostSuspicious(){
        Integer currentMostSuspicious=0;
        int currentHits=0;

        for(Integer key:suspicions.keySet()){
            int hits=suspicions.get(key);
            if (hits>currentHits){
                currentHits=hits;
                currentMostSuspicious=key;
            }
        }

        return currentMostSuspicious;
    }

    public void addVote(){
        totalVotes++;

    }
    public void addInteract(){
        totalInteracts++;

    }


    boolean guessedCorrectly(){
        return secretID==getMostSuspicious();
    }

    void printIdentityData() {

        System.out.println("RealID=" + secretID + ", guessedID=" + getMostSuspicious());
        System.out.println("Interacts:" + totalInteracts);
        System.out.println("Votes:" +totalVotes);
    }





}

this was written as quickly as possible, it is neither efficient nor uses good practice

  • 2
    And the simplicity of identifying who-is-who would increase with the reputation. There are only so many users with >100K rep, and even fewer with 400K+ so it would be very easy to figure out Darin's or Jon's voting patterns. – psubsee2003 Feb 3 '14 at 15:22
  • @Richard; Thank you for your thorough and motivated response! Yes, I have considered the implications of reverse engineering anonymous data and it is a valid point. I haven't looked closely at your model, but the results are convincing that it is very feasible to make strong predictions. I imagine a way to safe guard against the approach you have taken to parse users' true identities could be to not include the postID to which the votes are attributed. Or even better, a dataset which simply provides reputation and voting information for each vote. – tuntun Feb 4 '14 at 15:37
  • @tuntun Those steps would certainly make the data safer, but also less useful. I can understand why you would want this data and if it were possible to access safely I would support it, but getting it out safely would be a real challenge – Richard Tingle Feb 4 '14 at 15:39
17

Since voting is an anonymous activity for obvious reasons

and...

My aim is to develop a who-votes-for-who network

Do you see how those don't go together? Yeah we're not giving this data out, for obvious reasons.

  • I don't find it obvious at all that voting is anonymous. I think it would be much better if we knew who voted to whom. – H2CO3 Feb 3 '14 at 19:40
  • 2
    @H2CO3 I present as evidence ebay; everyone has 99% positive reputation because of revenge voting. Furthmore you'd get "this answered your question, why haven't you upvoted me" – Richard Tingle Feb 3 '14 at 20:44
  • @Nick Craver; I'm not sure that you fully understood my approach based on your answer. Perhaps I should have posed the question in more detail. – tuntun Feb 4 '14 at 15:04
  • In fact, yours was a fairly condescending response with no real answer. I am interested that a cogent argument, as was presented by Richard below, has less votes than your answer. Perhaps it is too soon, but it is interesting to note that you have a higher reputation than he does and also have received more votes on this instance. This is exactly the behavior I am interested in. – tuntun Feb 4 '14 at 15:47
  • @tuntun The votes in this case are the thoughts of the people on meta, we have had this request many times before. Also as a developer that works here (and specifically handles SEDE and the data dumps), I can give an official response to this - that's what is being upvoted. I didn't give a super thorough response because this has already been asked and answered, many times. – Nick Craver Feb 4 '14 at 15:51
  • @Nick Craver I completely understand your concern. Since the voter ID is necessary for our research, is it possible to sign a privacy agreement with you for not leaking these information to others ? The result will be published in an aggregated way and for academic purpose only. – Luke Liu Mar 12 '15 at 15:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .