1

I'm creating a .txt file that has four soldiers on a team ranking each other in order of merit. Soldiers do not rank themselves.

I have the following code:

import itertools

# create a list of 4 soldiers
numSoldiers=4
soldiers = []
for i in range(1,numSoldiers+1):
    soldiers.append('soldier'+str(i))

# create list of all permutations of 2 soldiers
perms = list(itertools.permutations(soldiers, 2))

# list of possible rankings (soldier does not rank himself/herself)
possRanks = list(range(1,len(soldiers)))

# create the edgelist .txt file with randomized rankings
open('peerRankingsEdgelist.txt', 'w').close() #delete contents of .txt file at the start

file = open('peerRankingsEdgelist.txt', 'w')

for i in perms:
    file.write(i[0] + ', ' + i[1] + '\n')

file.close() #close file

which produces the following output in a .txt file:

soldier1, soldier2
soldier1, soldier3
soldier1, soldier4
soldier2, soldier1
soldier2, soldier3
soldier2, soldier4
soldier3, soldier1
soldier3, soldier2
soldier3, soldier4
soldier4, soldier1
soldier4, soldier2
soldier4, soldier3

For each set of three lines (the sets of lines that start with the same soldier), I want to add a randomized ranking that comes from possRanks, where possRanks = [1, 2, 3]. The problem is that I can't completely randomize it because then the rankings might repeat. For example, soldier1 might rank both soldier2 and soldier3 with a ranking of 1, which I cannot have.

A correct example output would be the following:

soldier1, soldier2, 3
soldier1, soldier3, 1
soldier1, soldier4, 2
soldier2, soldier1, 1
soldier2, soldier3, 2
soldier2, soldier4, 3
soldier3, soldier1, 2
soldier3, soldier2, 1
soldier3, soldier4, 3
soldier4, soldier1, 1
soldier4, soldier2, 2
soldier4, soldier3, 3

Here, soldier1 ranks soldier2 with a value of 3, soldier3 with a value of 1, and soldier4 with a value of 2.

jfeuerman
  • 177
  • 9
  • What is the question? – wwii Apr 30 '20 at 03:27
  • How do I append a randomized, non-repeating ranking from `possRanks` to each set of three lines in the .txt file? – jfeuerman Apr 30 '20 at 03:28
  • 1
    `random.shuffle` can be useful here. – Michael Butscher Apr 30 '20 at 03:30
  • Does this answer your question? [Pick N distinct items at random from sequence of unknown length, in only one iteration](https://stackoverflow.com/questions/9690009/pick-n-distinct-items-at-random-from-sequence-of-unknown-length-in-only-one-ite) ... there are other possible answers searching with variations of `python choose random items from a list without repeating site:stackoverflow.com` – wwii Apr 30 '20 at 03:33

2 Answers2

1

Using your file as an example data.txt:

soldier1, soldier2
soldier1, soldier3
soldier1, soldier4
soldier2, soldier1
soldier2, soldier3
soldier2, soldier4
soldier3, soldier1
soldier3, soldier2
soldier3, soldier4
soldier4, soldier1
soldier4, soldier2
soldier4, soldier3

We could read the file in N sized chunks using the itertools grouper recipe, shuffle possRanks with random.shuffle, then zip each group with each shuffled rank. We can then open a new file to write to, such as output.txt.

from random import shuffle
from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

possRanks = [1, 2, 3]

with open("data.txt") as f, open("output.txt", mode="w") as o:
    for line in grouper(map(str.strip, f), len(possRanks), ''):
        shuffle(possRanks)
        for group, rank in zip(line, possRanks):
            o.write(f"{group}, {rank}\n")

Which will write a random shuffling of ranks to output.txt every time you run the above:

soldier1, soldier2, 2
soldier1, soldier3, 1
soldier1, soldier4, 3
soldier2, soldier1, 1
soldier2, soldier3, 3
soldier2, soldier4, 2
soldier3, soldier1, 2
soldier3, soldier2, 3
soldier3, soldier4, 1
soldier4, soldier1, 3
soldier4, soldier2, 1
soldier4, soldier3, 2
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
1

Here is another way just using enum which might be easier to parse;

import itertools
import random

# create a list of 4 soldiers
numSoldiers=4
soldiers = []
for i in range(1,numSoldiers+1):
    soldiers.append('soldier'+str(i))

# create list of all permutations of 2 soldiers
perms = list(itertools.permutations(soldiers, 2))

# list of possible rankings (soldier does not rank himself/herself)
possRanks = list(range(1,len(soldiers)))

# create the edgelist .txt file with randomized rankings
open('peerRankingsEdgelist.txt', 'w').close() #delete contents of .txt file at the start

file = open('peerRankingsEdgelist.txt', 'w')

for num, i in enumerate(perms):
    if num % len(possRanks) == 0:
        shuffled_ranks = possRanks.copy()
        random.shuffle(shuffled_ranks)

    random_rank = str(shuffled_ranks.pop())

    line = (i[0]) + ', ' + i[1] + ',' + random_rank + '\n'


    file.write(line)

file.close() #close file

So the only part I have changed is here:

for num, i in enumerate(perms):
        if num % len(possRanks) == 0:
            shuffled_ranks = possRanks.copy()
            random.shuffle(shuffled_ranks)

        random_rank = str(shuffled_ranks.pop())

        line = (i[0]) + ', ' + i[1] + ',' + random_rank + '\n'

Basically I am using enumerate to count each iteration, and am only creating new random list of possRanks every x amount of iterations, where x is equal to the len of possRanks. We then just pop each item from the list, basically exhausting the list every x iteration, and create a new list after exhaustion.

Moondra
  • 4,399
  • 9
  • 46
  • 104