I am fairly new to Python and do not understand the explanation given by the course I am doing. I cannot follow why width gets 2.
To my understanding the print(combine(1)[2]) appoints the value to to position one. But I thought is_3D is in position 0, hence height would be in position 2.
So I do not understand what is going on here.
def combine(width, height=2, depth=0, is_3D=False):
return[is_3D, width, height, depth]
print(combine(1)[2])
You call combine() with a value of 1. Thus, within the function, width will be equal to 1. All other local variables will take their default values.
You return a list of values. The height value (which defaulted to 2) is at index 2 in that list.
That's why the output of your print is 2 - i.e., it's the (default) value of height
Here's a code answering your question, see below.
Short answer - return[is_3D, width, height, depth] reorders the input parameters of this function inside the list. At the end, you are asking to print an element in position [2], which is 'height'. Height has a default value of 2 and is a keyword argument with a default value of 2. Which results in the output of 2.
I added logging to the code, which is best practice and can be useful.
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
def combine(width, height=2, depth=0, is_3D=False):
logging.info(f'width = {width}')
logging.info(f'height = {height}')
logging.info(f'height = {depth}')
logging.info(f'is_3D = {is_3D}')
return [is_3D, width, height, depth]
returned_list = combine(1)
logging.info(f'return of the function is {returned_list}')
print(returned_list[2])
2023-02-03 13:24:52,069 - root - INFO - width = 1
2023-02-03 13:24:52,069 - root - INFO - height = 2
2023-02-03 13:24:52,069 - root - INFO - height = 0
2023-02-03 13:24:52,069 - root - INFO - is_3D = False
2023-02-03 13:24:52,069 - root - INFO - return of the function is [False, 1, 2, 0]
2
Related
I am looking for ideas on how to translate one range values to another in Python. I am working on hardware project and am reading data from a sensor that can return a range of values, I am then using that data to drive an actuator that requires a different range of values.
For example lets say that the sensor returns values in the range 1 to 512, and the actuator is driven by values in the range 5 to 10. I would like a function that I can pass a value and the two ranges and get back the value mapped to the second range. If such a function was named translate it could be used like this:
sensor_value = 256
actuator_value = translate(sensor_value, 1, 512, 5, 10)
In this example I would expect the output actuator_value to be 7.5 since the sensor_value is in the middle of the possible input range.
One solution would be:
def translate(value, leftMin, leftMax, rightMin, rightMax):
# Figure out how 'wide' each range is
leftSpan = leftMax - leftMin
rightSpan = rightMax - rightMin
# Convert the left range into a 0-1 range (float)
valueScaled = float(value - leftMin) / float(leftSpan)
# Convert the 0-1 range into a value in the right range.
return rightMin + (valueScaled * rightSpan)
You could possibly use algebra to make it more efficient, at the expense of readability.
Using scipy.interpolate.interp1d
You can also use scipy.interpolate package to do such conversions (if you don't mind dependency on SciPy):
>>> from scipy.interpolate import interp1d
>>> m = interp1d([1,512],[5,10])
>>> m(256)
array(7.4951076320939336)
or to convert it back to normal float from 0-rank scipy array:
>>> float(m(256))
7.4951076320939336
You can do also multiple conversions in one command easily:
>>> m([100,200,300])
array([ 5.96868885, 6.94716243, 7.92563601])
As a bonus, you can do non-uniform mappings from one range to another, for intance if you want to map [1,128] to [1,10], [128,256] to [10,90] and [256,512] to [90,100] you can do it like this:
>>> m = interp1d([1,128,256,512],[1,10,90,100])
>>> float(m(400))
95.625
interp1d creates piecewise linear interpolation objects (which are callable just like functions).
Using numpy.interp
As noted by ~unutbu, numpy.interp is also an option (with less dependencies):
>>> from numpy import interp
>>> interp(256,[1,512],[5,10])
7.4951076320939336
This would actually be a good case for creating a closure, that is write a function that returns a function. Since you probably have many of these values, there is little value in calculating and recalculating these value spans and factors for every value, nor for that matter, in passing those min/max limits around all the time.
Instead, try this:
def make_interpolater(left_min, left_max, right_min, right_max):
# Figure out how 'wide' each range is
leftSpan = left_max - left_min
rightSpan = right_max - right_min
# Compute the scale factor between left and right values
scaleFactor = float(rightSpan) / float(leftSpan)
# create interpolation function using pre-calculated scaleFactor
def interp_fn(value):
return right_min + (value-left_min)*scaleFactor
return interp_fn
Now you can write your processor as:
# create function for doing interpolation of the desired
# ranges
scaler = make_interpolater(1, 512, 5, 10)
# receive list of raw values from sensor, assign to data_list
# now convert to scaled values using map
scaled_data = map(scaler, data_list)
# or a list comprehension, if you prefer
scaled_data = [scaler(x) for x in data_list]
I was looking for the same thing in python to map angles 0-300deg to raw dynamixel values 0-1023, or 1023-0 depending on the actuator orientations.
I ended up going very simple.
Variables:
x:input value;
a,b:input range
c,d:output range
y:return value
Function:
def mapFromTo(x,a,b,c,d):
y=(x-a)/(b-a)*(d-c)+c
return y
Usage:
dyn111.goal_position=mapFromTo(pos111,0,300,0,1024)
def translate(sensor_val, in_from, in_to, out_from, out_to):
out_range = out_to - out_from
in_range = in_to - in_from
in_val = sensor_val - in_from
val=(float(in_val)/in_range)*out_range
out_val = out_from+val
return out_val
Simple map range function:
def mapRange(value, inMin, inMax, outMin, outMax):
return outMin + (((value - inMin) / (inMax - inMin)) * (outMax - outMin))
def maprange(a, b, s):
(a1, a2), (b1, b2) = a, b
return b1 + ((s - a1) * (b2 - b1) / (a2 - a1))
a = [from_lower, from_upper]
b = [to_lower, to_upper]
found at https://rosettacode.org/wiki/Map_range#Python_
does not clamp the transformed values to the ranges a or b (it extrapolates)
also works when from_lower > from_upper or to_lower > to_upper
All of the existing answers are under the CC BY-SA license. Here's one that I wrote; to the extent possible under law, I waive all copyright or related and neighboring rights to this code. (Creative Commons CC0 Public Domain Dedication).
def remap(number, from_min, from_max, to_min, to_max):
number_s = number - from_min
from_max_s = from_max - from_min
to_max_s = to_max - to_min
return ((number_s / from_max_s) * to_max_s) + to_min
You could use a lambda function
translate = lambda a, b, c, d, e: (a - b) * (e - d) / (c - b) + d
sensor_value = 256
translate(sensor_value, 1, 512, 5, 10)
>> 7.495107632093934
I am doing a maze game so far I have the following code, which reads the positions of a text file the obstacles that must be placed, however I do not have extensive knowledge about pygame to display them on the screen.
def set_walls(self, walls):
walls_length = len(walls)
for i in range(walls_length):
# First case is row...
if "row" in walls[i]:
row_index = int(re.sub("[^0-9]", "", walls[i]))
print(row_index)
column_indexes = walls[i+1].split()
print(column_indexes)
for index in column_indexes:
self.wall_vertical[row_index - 1][int(index) - 1] = 1
# Second case is column...
elif "column" in walls[i]:
column_index = int(re.sub("[^0-9]", "", walls[i]))
row_indexes = walls[i + 1].split()
for index in row_indexes:
self.walls_horizontal[int(index) - 1][column_index - 1] = 1
If you have the indexes for each wall, you can multiply them by the wall width and height, this should place all the walls in the right places. I can't help much more seeing as how you did not provide a text file sample.
x = x_index * wall_width
y = y_index * wall_height
I'm attempting to create a function that creates a rectangle made of an inputted character. So,
def Make_character_rectangle(height, width, char):
char = str(char)
while height > 0:
height = height-1
print(t * width)
So Make _character_rectangle(3, 2, %) should make:
%%
%%
%%
however when i input a symbol such as % into the function, it says invalid syntax. I tried converting char into a string but it still gives the same error.
def Make_character_rectangle(height, width, char):
char = str(char)
while height > 0:
height = height-1
print(char * width)
Not sure what 't' was, but the above function now produces
>>> Make_character_rectangle(2,3,'%')
%%%
%%%
I don't see where you defined t in print(t * width). Change the t to char.
You can also change height = height - 1 to height -= 1.
When I run this, the end output is a table with columns:
Vertex - DisVal - PrevVal - Known.
The two nodes connected to my beginning node show the correct values, but none of the others end up getting updated. I can include the full program code if anyone wants to see, but I know the problem is isolated here. I think it may have to do with not changing the index the right way. This is a simple dijsktra's btw, not the heap/Q version.
Here's the rest of the code: http://ideone.com/UUOUn8
The adjList looks like this: [1: 2, 4, 2: 6, 3, ...] where it shows each node connected to a vertex. DV = distance value (weight), PV = previous value (node), known = has it bee visited
def dijkstras(graph, adjList):
pv = [None] * len(graph.nodes)
dv = [999]*len(graph.nodes)
known = [False] * len(graph.nodes)
smallestV = 9999
index = 0
dv[0] = 0
known[0] = True
for i in xrange(len(dv)):
if dv[i] < smallestV and known[i]:
smallestV = dv[i]
index = i
known[index] = True
print smallestV
print index
for edge in adjList[index]:
if (dv[index]+graph.weights[(index, edge)] < dv[edge]):
dv[edge] = dv[index] + graph.weights[(index, edge)]
pv[edge] = index
printTable(dv, pv, known)
The first iteration sets smallestV and index to 0 unconditionally, and they never change afterwards (assuming non-negative weights).
Hard to tell what you are trying to do here.
I'm a python n00b and I'd like some suggestions on how to improve the algorithm to improve the performance of this method to compute the Jaro-Winkler distance of two names.
def winklerCompareP(str1, str2):
"""Return approximate string comparator measure (between 0.0 and 1.0)
USAGE:
score = winkler(str1, str2)
ARGUMENTS:
str1 The first string
str2 The second string
DESCRIPTION:
As described in 'An Application of the Fellegi-Sunter Model of
Record Linkage to the 1990 U.S. Decennial Census' by William E. Winkler
and Yves Thibaudeau.
Based on the 'jaro' string comparator, but modifies it according to whether
the first few characters are the same or not.
"""
# Quick check if the strings are the same - - - - - - - - - - - - - - - - - -
#
jaro_winkler_marker_char = chr(1)
if (str1 == str2):
return 1.0
len1 = len(str1)
len2 = len(str2)
halflen = max(len1,len2) / 2 - 1
ass1 = '' # Characters assigned in str1
ass2 = '' # Characters assigned in str2
#ass1 = ''
#ass2 = ''
workstr1 = str1
workstr2 = str2
common1 = 0 # Number of common characters
common2 = 0
#print "'len1', str1[i], start, end, index, ass1, workstr2, common1"
# Analyse the first string - - - - - - - - - - - - - - - - - - - - - - - - -
#
for i in range(len1):
start = max(0,i-halflen)
end = min(i+halflen+1,len2)
index = workstr2.find(str1[i],start,end)
#print 'len1', str1[i], start, end, index, ass1, workstr2, common1
if (index > -1): # Found common character
common1 += 1
#ass1 += str1[i]
ass1 = ass1 + str1[i]
workstr2 = workstr2[:index]+jaro_winkler_marker_char+workstr2[index+1:]
#print "str1 analyse result", ass1, common1
#print "str1 analyse result", ass1, common1
# Analyse the second string - - - - - - - - - - - - - - - - - - - - - - - - -
#
for i in range(len2):
start = max(0,i-halflen)
end = min(i+halflen+1,len1)
index = workstr1.find(str2[i],start,end)
#print 'len2', str2[i], start, end, index, ass1, workstr1, common2
if (index > -1): # Found common character
common2 += 1
#ass2 += str2[i]
ass2 = ass2 + str2[i]
workstr1 = workstr1[:index]+jaro_winkler_marker_char+workstr1[index+1:]
if (common1 != common2):
print('Winkler: Wrong common values for strings "%s" and "%s"' % \
(str1, str2) + ', common1: %i, common2: %i' % (common1, common2) + \
', common should be the same.')
common1 = float(common1+common2) / 2.0 ##### This is just a fix #####
if (common1 == 0):
return 0.0
# Compute number of transpositions - - - - - - - - - - - - - - - - - - - - -
#
transposition = 0
for i in range(len(ass1)):
if (ass1[i] != ass2[i]):
transposition += 1
transposition = transposition / 2.0
# Now compute how many characters are common at beginning - - - - - - - - - -
#
minlen = min(len1,len2)
for same in range(minlen+1):
if (str1[:same] != str2[:same]):
break
same -= 1
if (same > 4):
same = 4
common1 = float(common1)
w = 1./3.*(common1 / float(len1) + common1 / float(len2) + (common1-transposition) / common1)
wn = w + same*0.1 * (1.0 - w)
return wn
Example output
ZIMMERMANN ARMIENTO 0.814583333
ZIMMERMANN ZIMMERMANN 1
ZIMMERMANN CANNONS 0.766666667
CANNONS AKKER 0.8
CANNONS ALDERSON 0.845833333
CANNONS ALLANBY 0.833333333
I focused more on optimizing to get more out of Python than on optimizing the algorithm because I don't think that there is much of an algorithmic improvement to be had here. Here are some Python optimizations that I came up with.
(1). Since you appear to be using Python 2.x, change all range()'s to xrange()'s. range() generates the full list of numbers before iterating over them while xrange generates them as needed.
(2). Make the following substitutions for max and min:
start = max(0,i-halflen)
with
start = i - halflen if i > halflen else 0
and
end = min(i+halflen+1,len2)
with
end = i+halflen+1 if i+halflen+1 < len2 else len2
in the first loop and similar ones for the second loop. There's also another min() farther down and a max() near the beginning of the function so do the same with those. Replacing the min()'s and max()'s really helped to reduce the time. These are convenient functions, but more costly than the method I've replaced them with.
(3). Use common1 instead of len(ass1). You've kept track of the length of ass1 in common1 so let's use it rather than calling a costly function to find it again.
(4). Replace the following code:
minlen = min(len1,len2)
for same in xrange(minlen+1):
if (str1[:same] != str2[:same]):
break
same -= 1
with
for same in xrange(minlen):
if str1[same] != str2[same]:
break
The reason for this is mainly that str1[:same] creates a new string every time through the loop and you will be checking parts that you've already checked. Also, there's no need to check if '' != '' and decrement same afterwards if we don't have to.
(5). Use psyco, a just-in-time compiler of sorts. Once you've downloaded it and installed it, just add the lines
import psyco
psyco.full()
at the top of the file to use it. Don't use psyco unless you do the other changes that I've mentioned. For some reason, when I ran it on your original code it actually slowed it down.
Using timeit, I found that I was getting a decrease in time of about 20% or so with the first 4 changes. However, when I add psyco along with those changes, the code is about 3x to 4x faster than the original.
If you want more speed
A fair amount of the remaining time is in the string's find() method. I decided to try replacing this with my own. For the first loop, I replaced
index = workstr2.find(str1[i],start,end)
with
index = -1
for j in xrange(start,end):
if workstr2[j] == str1[i]:
index = j
break
and a similar form for the second loop. Without psyco, this slows down the code, but with psyco, it speeds it up quite a lot. With this final change the code is about 8x to 9x faster than the original.
If that isn't fast enough
Then you should probably turn to making a C module.
Good luck!
I imagine you could do even better if you used the PyLevenshtein module. It's C and quite fast for most use cases. It includes a jaro-winkler function that gives the same output, but on my machine it's 63 times faster.
In [1]: import jw
In [2]: jw.winklerCompareP('ZIMMERMANN', 'CANNONS')
Out[2]: 0.41428571428571426
In [3]: timeit jw.winklerCompareP('ZIMMERMANN', 'CANNONS')
10000 loops, best of 3: 28.2 us per loop
In [4]: import Levenshtein
In [5]: Levenshtein.jaro_winkler('ZIMMERMANN', 'CANNONS')
Out[5]: 0.41428571428571431
In [6]: timeit Levenshtein.jaro_winkler('ZIMMERMANN', 'CANNONS')
1000000 loops, best of 3: 442 ns per loop
In addition to everything that Justin says, concatenating strings is expensive - python has to allocate memory for the new string then copy both strings into it.
So this is bad:
ass1 = ''
for i in range(len1):
...
if (index > -1): # Found common character
...
ass1 = ass1 + str1[i]
It will probably be faster to make ass1 and ass2 lists of characters and use ass1.append(str1[i]). As far as I can see from my quick read of the code the only thing you do with ass1 and ass2 afterwards is to iterate through them character by character so they do not need to be strings. If you did need to use them as strings later then you can convert them with ''.join(ass1).