Skip to main content
4 of 4
nearly all the answers are about how to better use numpy
Snowbody
  • 8.5k
  • 23
  • 50

Estimating the number of tanks based on a sample of serial numbers 2.0

There was some confusion with the code I had posted in my previous version of this question and there was some good advice from @Oscar Smith.

The explanation is the same; please give improvements for speed in this new version:

#!/usr/bin/python

import numpy as np
import time

chosenNum = 429
numRuns = 10000
numCapturedTanks = 7
numGuessN = []
guesses = []
percentErrors = []
STDarray = []
start_time = time.time()
STDtimes = []

def getAverageStdTime(timetaken): # gets the average time it took to calculate standard deviations
  STDtimes.append(timetaken)
  if (len(STDtimes) == numRuns):
    print ("Average List of Standard Devations Generation Time: " + str(round(np.mean(STDtimes),2)) + " seconds")

def createListOfStandardDeviations(start,end):
    for y in range(start,int(end)):
        tankSerialNumbersSimulated = np.random.randint(1, y + 1, size=numCapturedTanks) #from Oscar Smith
        simulatedSTD = np.std(tankSerialNumbersSimulated)
        STDarray.append(simulatedSTD)

def getAllGuesses():
    print ("Your guesses are: " + str(guesses))

def getAvgPercentError():
    numCorrect = 0
    closestNumber = 0
    for x in range(len(guesses) - 1):
        percentError = '%.2f' % round(((np.abs(guesses[x] - chosenNum))/float(chosenNum) * 100), 2)
        percentErrors.append(float(percentError))
        if(guesses[x] == chosenNum):
            numCorrect = numCorrect + 1
        else:
            closestNumber = min(guesses, key=lambda x:abs(x-chosenNum))
    averagePercentError = np.mean(percentErrors)
    print ("The average Percent Error is: " + str(round(averagePercentError,2)) + "%")
    getAccuracy(numCorrect,closestNumber)

def getAccuracy(amountCorrect,closestNumberToActual):
    if (amountCorrect > 0):
        print ("You got the correct number " + str(amountCorrect) + " out of " + str(len(guesses)) + " times.")
    else:
        print ("Your closest number was: " + str(closestNumberToActual))
    getmode(guesses)

def getmode(inplist):
    dictofcounts = {}
    listofcounts = []
    for i in inplist:
        countofi = inplist.count(i) # count items for each item in list
        listofcounts.append(countofi) # add counts to list
        dictofcounts[i]=countofi # add counts and item in dict to get later
    maxcount = max(listofcounts) # get max count of items
    if maxcount ==1:
        print ("There is no mode for this dataset, values occur only once")
    else:
        modelist = [] # if more than one mode, add to list to print out
        for key, item in dictofcounts.items():
            if item ==maxcount: # get item from original list with most counts
                modelist.append(str(key))
        print ("Most guessed number(s):",' and '.join(modelist))
        return modelist

def getNumGuessed(givenSTD,maxNumber):
    minStd = min(STDarray, key=lambda x:abs(x-givenSTD)) #finds closest standard deviation to the given standard deviation
    for (z,this_std) in enumerate(STDarray):
        if(minStd == this_std): #find closest number to original standard deviation
            numGuessed = z + maxNumber
            return numGuessed

def main():
    print ("reached main")
    for runsRan in range(numRuns):
        tankSerialNumbers = np.random.randint(1, chosenNum + 1, size=numCapturedTanks) #from Oscar Smith
        NumSTD = np.std(tankSerialNumbers)
        highestTankSerial = np.mean(tankSerialNumbers) + 3*NumSTD
        maxNum = np.amax(tankSerialNumbers)
        print ("Tank Serial Numbers Generated")
        print ("Standard Deviation and Range Calculated")
        ListOfStandardDeviationsStartTime = time.time()
        for _ in range(100):
            del STDarray[:]
            if (maxNum - highestTankSerial < 0):
                createListOfStandardDeviations(maxNum,highestTankSerial)
            else:
                createListOfStandardDeviations(highestTankSerial,maxNum)
            numGuessN.append(getNumGuessed(NumSTD,maxNum))
        print ("Initial List of Standard Deviations Generated")
        print ("List of Standard Devations Generation took " + str(round(time.time() - ListOfStandardDeviationsStartTime,2)) + " seconds")

        guess = int(np.mean(numGuessN))
        print ("Guess Generated " + str(runsRan + 1))
        getAverageStdTime(float(time.time() - ListOfStandardDeviationsStartTime))
        guesses.append(guess)
    getAllGuesses()
    getAvgPercentError()

main()
print ("My program took " + str(round((time.time() - start_time)/float(60),2)) + " minutes to run")

Currently, the runtime is approximately 7.26 minutes for 1,000 runs. I want to get it to run 10,000 times and at this rate, it would take too long.

If anyone is confused by what the purpose is or any part of the code, please mention specifically what is confusing and I'll explain it.

To clarify: We are given 7 serial numbers based on a value n which is unknown (this number is given only to check how good our process of determining n is and to generate the 7 random serial numbers from it).

My process first finds the standard deviation of the given serial numbers. Then I find a limit that n definitely cannot exceed (three standard deviations above the mean) and a max from the given list as what it can't be below. I then simulate what random serial numbers would be generated from the predicted n from the range I found out. I take the standard deviation of each simulation and find which one is the closest to the standard deviation of the given serial numbers and store the corresponding guessed n. I do this x times (the more the better - I used 100) to get x guessed n 's. I take the mean of those guesses to get my final guess. I then find the percent error of my guess based on the actual number.