4
\$\begingroup\$

This is an update of Test thousands of combinations quickly.

I have a physical robot which has a function, turn(degrees). For different robots, there will be small differences in the accuracy of the turn. I have fine turning variables, multiplier and adder. The degrees are multiplied by the multiplier, and then adder is added on.

The data set will be manually created by someone requesting a turn, and recording what the actual turn was. This means the data set will usually be around 4 to 8 results long.

The PRECISION is how accurate we want the result to be. Ideally it would be as precise as possible, but since the robot has a fair amount of unpredictability, 2 d.p is fine.

The Code:

from numpy import arange

# list of actual test results in the form
# (requested turn degrees, actual turn degrees) 
REAL_RESULTS = (
    (90, 101),
    (100, 112),
    (110, 123),
    (120, 134),
    (130, 145),
)
# how accurate a multiplier you want
PRECISION = 0.001
# multiplier will be less than this
# and more than negative this
MULTIPLIER_BOUNDS = 10
# number of results to print
PRINT_TOP_RESULTS = 3


def calculateLine(multiplier, test_cases):
    """Calculates the absolute error and adder."""
    # calculate the error with no adder
    error_no_adder = 0
    for test in test_cases:
        error_no_adder -= test[0] * multiplier - test[1]
    # the adder is the average error
    adder = error_no_adder / len(REAL_RESULTS)

    # calculate error with the adder
    error = 0
    for test in test_cases:
        error += abs((test[1] - adder) / multiplier - test[0])

    return error, adder, multiplier

if __name__ == "__main__":
    # calculate results for every multiplier
    results = [calculateLine(multiplier, REAL_RESULTS) for multiplier in
               arange(-MULTIPLIER_BOUNDS, MULTIPLIER_BOUNDS, PRECISION)]
    # sort from lowest error to highest error
    results.sort()

    printed_num = 0
    i = -1
    last_multiplier = 999
    # print the top (least error) x results 
    while printed_num < PRINT_TOP_RESULTS:
        i += 1
        # don't print results with very similar precision
        if abs(results[i][2] - last_multiplier) < PRECISION * 20:
            continue
        printed_num += 1
        last_multiplier = results[i][2]
        # print average error, the adder and multiplier
        print("Average error: {:.3f}\nAdder: {:.3f}\nMultiplier: {:.3f}\n"
              .format(*results[i]))

Have I followed standard coding conventions? pep8online says that it is fine.

Is it "better" - performance or conventions - to map the results or use list comprehension? I had previously used map and the calculate function used the global variable REAL_RESULTS, but I changed this because it defeats the purpose of reusable modules if a function relies on predefined global variables.

Are there any areas where performance could be improved? Performance isn't really an issue anymore because it has been hugely improved from my original code, but it's still nice to know if it could be better. Also this code could theoretically be used with a much larger data set where performance could matter.

\$\endgroup\$
2
\$\begingroup\$

PEP-8 issue: Function names should be lowercase_with_underscores.

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ Haha thanks, the name change was a quick fix for being non-descriptive (before it was just called calculate). I won't update the question as this would invalidate your answer. \$\endgroup\$ – DarkMatterMatt Jun 23 '17 at 6:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.