Search code examples
pythonrandomtextpython-itertoolsedge-list

Adding (non-repeating) Random Numbers to Each Line of .txt File


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.


Solution

  • 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