How to plot function results with matplotlib in python - python

I have made a function which returns a value for a force depending on the z position (z_pos). I would like to plot these results (shear diagram for the engineers here), however I get the following error:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
I have tried it both with arange and linspace, see the code here:
import matplotlib.pyplot as plt
import numpy as np
#values in kN and m
FyFL = 520
FyRL = 1246
L = 40.
Lf1 = 2.
Lf2 = 25.5
g = 9.81
W = 60000
q = (3*g*W/L)/1000 #kN/m
print q
def int_force_y(FyFL, FyRL, L, Lf1, Lf2, q, z_pos):
if z_pos <= Lf1:
int_fc_y = -q*z_pos
elif z_pos > Lf1 and z_pos < Lf1+Lf2:
int_fc_y = -q*Lf1 + FyFL-q*z_pos
elif z_pos >= Lf2 and z_pos <= 40.:
int_fc_y = -q*Lf1 + FyFL-q*(Lf1+Lf2)-q*z_pos
else:
return "No valid z_pos"
return int_fc_y
z_pos = np.arange(0,41,1)
y = int_force_y(FyFL, FyRL, L, Lf1, Lf2, q, z_pos)
plt.plot(z_pos,y)
plt.show()
Help is very much appreciated!

The error you are getting has nothing to do with the plotting but arises when you call int_force_y. The argument z_pos is a np.ndarray. If you now compare this to eg. Lf1 in your function then this gives you a boolean array where each element indicates if the corresponding element of z_pos is smaller or equal to Lf1 in case of your first if statement. As some elements are smaller or equal and some are not, he cannot decide whether he should consider this as True or False and asks you to use .any() to indicate that it should be True if any element is True or .all() to indicate that it should be True if all elements are True.
But both or these cases do not do what you want them to do. You want a decision for each element individually and to then set the corresponding value in int_fc_y accordingly. You can do this by a for-loop or more elegantly by using boolean indexing and np.logical_and. Just use this function to produce your result array instead of your version:
def int_force_y(FyFL, FyRL, L, Lf1, Lf2, q, z_pos):
if (z_pos>40.).any():
return "No valid z_pos"
int_force_y = np.zeros_like(z_pos)
int_fc_y[z_pos<=Lf1] = -q*z_pos
int_fc_y[np.logical_and(z_pos > Lf1,
z_pos < Lf1+Lf2)] = -q*Lf1 + FyFL-q*z_pos
int_fc_y[np.logical_and(z_pos >= Lf2,
z_pos <= 40.)] = -q*Lf1 + FyFL-q*(Lf1+Lf2)-q*z_pos
return int_fc_y

The problem happens because you are asking python whether if an array is larger or smaller than certain value:
if z_pos <= Lf1:
This might be truth for some values and false for some others, with leaves the question of whether if that statement is true or false ambiguous.
You can try :
if np.array(z_pos <= Lf1).any():
or
if np.array(z_pos <= Lf1).all():
depending on what you want.
Same for the following if statements.

Related

How to perform comparison of float elements in a NumPy array to Python floats?

I am having a rather peculiar problem. I am trying to compare elements of a NumPy array to check if they are smaller than a given number. Here are some code snippets:
for i in range(len(K_list)):
K = K_list[i]
data = simulate(robot, e_l_e, e_h_e, v_e, w_e, theta_e, thetadot_e, tau_right_e, tau_left_e, K, turn_left, ground_pitch, initial_speed, initial_lateral_error, initial_heading_error, initial_pitch, dt = robot.dt)
pitch_angle = data['pitch_angle']
heading_error = data['heading_error']
lateral_error = data['lateral_error']
print('Data is collected')
print('Max pitch angle: ', np.max(pitch_angle), 'Limit = 2')
print('Max heading error: ', np.max(heading_error), 'Limit = 2.5')
print('Max lateral error: ', np.max(lateral_error), 'Limit = 0.5')
print(np.all(pitch_angle) < 2 == True)
print(np.all(heading_error) < 2.5 == True)
print(np.all(lateral_error) < 0.5 == True)
if (np.all(pitch_angle) < 2 and np.all(heading_error) < 2.5 and np.all(lateral_error) < 0.5) == True:
print('Data is valid')
K_valid.append(K)
Q_valid.append(Q_list[i])
R_valid.append(R_list[i])
else:
print('Data is invalid')
print(' ')
I am first printing out the maximum value of some NumPy arrays to check if they are really outside the bounds that I have defined. Then, if those arrays are within the defined bounds, I want to append it to a list I created (list creation not shown).
Here is the output I get:
Max pitch angle: 1.5707963267948966 Limit = 2
Max heading error: 3.1328759095969225 Limit = 2.5
Max lateral error: 0.31887738896495144 Limit = 0.5
False
False
False
Data is invalid
For instance, here, I expect the Boolean statements to be True, False, True.
What could be going wrong here? Is there a particular way to perform comparison of elements in a NumPy array?

Python internal metric unit conversion function

I'm trying to build a function to do internal metric conversion on a wavelength to frequency conversion program and have been having a hard time getting it to behave properly. It is super slow and will not assign the correct labels to the output. If anyone can help with either a different method of computing this or a reason on why this is happening and any fixes that I cond do that would be amazing!
def convert_SI_l(n):
if n in range( int(1e-12),int(9e-11)):
return n/0.000000000001, 'pm'
else:
if n in range(int(1e-10),int(9e-8)):
return n/0.000000001 , 'nm'
else:
if n in range(int(1e-7),int(9e-5)):
return n/0.000001, 'um'
else:
if n in range(int(1e-4),int(9e-3)):
return n/0.001, 'mm'
else:
if n in range(int(0.01), int(0.99)):
return n/0.01, 'cm'
else:
if n in range(1,999):
return n/1000, 'm'
else:
if n in range(1000,299792459):
return n/1000, 'km'
else:
return n , 'm'
def convert_SI_f(n):
if n in range( 1,999):
return n, 'Hz'
else:
if n in range(1000,999999):
return n/1000 , 'kHz'
else:
if n in range(int(1e6),999999999):
return n/1e6, 'MHz'
else:
if n in range(int(1e9),int(1e13)):
return n/1e9, 'GHz'
else:
return n, 'Hz'
c=299792458
i=input("Are we starting with a frequency or a wavelength? ( F / L ): ")
#Error statements
if i.lower() == ("f"):
True
else:
if not i.lower() == ("l"):
print ("Error invalid input")
#Cases
if i.lower() == ("f"):
f = float(input("Please input frequency (in Hz): "))
size_l = c/f
print(convert_SI_l(size_l))
if i.lower() == ("l"):
l = float(input("Please input wavelength (in meters): "))
size_f = ( l/c)
print(convert_SI_f(size_f))
You are using range() in a way that is close to how it is used in natural language, to express a contiguous segment of the real number line, as in in the range 4.5 to 5.25. But range() doesn't mean that in Python. It means a bunch of integers. So your floating-point values, even if they are in the range you specify, will not occur in the bunch of integers that the range() function generates.
Your first test is
if n in range( int(1e-12),int(9e-11)):
and I am guessing you wrote it like this because what you actually wanted was range(1e-12, 9e-11) but you got TypeError: 'float' object cannot be interpreted as an integer.
But if you do this at the interpreter prompt
>>> range(int(1e-12),int(9e-11))
range(0, 0)
>>> list(range(int(1e-12),int(9e-11)))
[]
you will see it means something quite different to what you obviously expect.
To test if a floating-point number falls in a given range do
if lower-bound <= mynumber <= upper-bound:
You don't need ranges and your logic will be more robust if you base it on fixed threshold points that delimit the unit magnitude. This would typically be a unit of one in the given scale.
Here's a generalized approach to all unit scale determination:
SI_Length = [ (1/1000000000000,"pm"),
(1/1000000000, "nm"),
(1/1000000, "um"),
(1/1000, "mm"),
(1/100, "cm"),
(1, "m"),
(1000, "km") ]
SI_Frequency = [ (1, "Hz"), (1000,"kHz"), (1000000,"MHz"), (1000000000,"GHz")]
def convert(n,units):
useFactor,useName = units[0]
for factor,name in units:
if n >= factor : useFactor,useName = factor,name
return (n/useFactor,useName)
print(convert(0.0035,SI_Length)) # 3.5 mm
print(convert(12332.55,SI_Frequency)) # 12.33255 kHz
Each unit array must be in order of smallest to largest multiplier.
EDIT: Actually, range is a function which is generally used in itaration to generate numbers. So, when you write if n in range(min_value, max_value), this function generates all integers until it finds a match or reach the max_value.
The range type represents an immutable sequence of numbers and is commonly used for looping a specific number of times in for loops.
Instead of writing:
if n in range(int(1e-10),int(9e-8)):
return n/0.000000001 , 'nm'
you should write:
if 1e-10 <= n < 9e-8:
return n/0.000000001 , 'nm'
Also keep in mind that range only works on integers, not float.
More EDIT:
For your specific use case, you can define dictionary of *(value, symbol) pairs, like below:
import collections
symbols = collections.OrderedDict(
[(1e-12, u'p'),
(1e-9, u'n'),
(1e-6, u'μ'),
(1e-3, u'm'),
(1e-2, u'c'),
(1e-1, u'd'),
(1e0, u''),
(1e1, u'da'),
(1e2, u'h'),
(1e3, u'k'),
(1e6, u'M'),
(1e9, u'G'),
(1e12, u'T')])
The use the bisect.bisect function to find the "insertion" point of your value in that ordered collection. This insertion point can be used to get the simplified value and the SI symbol to use.
For instance:
import bisect
def convert_to_si(value):
if value < 0:
value, symbol = convert_to_si(-value)
return -value, symbol
elif value > 0:
orders = list(symbols.keys())
order_index = bisect.bisect(orders, value / 10.0)
order = orders[min(order_index, len(orders) - 1)]
return value / order, symbols[order]
else:
return value, u""
Demonstration:
for value in [1e-12, 3.14e-11, 0, 2, 20, 3e+9]:
print(*convert_to_si(value), sep="")
You get:
1.0p
0.0314n
0
2.0
2.0da
3.0G
You can adapt this function to your needs…

Sorted method: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

I have a problem using sorted() method. I am using this method inside a loop to sort a list which I am upgrading in every step of the loop. The first iteration works but the second an beyond doesn't and give me the next error:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Here is my code:
import numpy as np
import random as rd
import math
Poblacion = 10
pressure = int(0.3*Poblacion)
mutation_chance = 0.08
Modelo = np.array([[0.60,0.40,0.90],[0.26,0.20,0.02],[0.80,0.00,0.05]])
x = np.array([[1.0,2.0,3.0],[0.70,0.50,0.90],[0.10,0.40,0.20]])
y = np.array([[4.10,0.72,2.30],[1.43,0.30,1.01],[0.40,0.11,0.18]])
def crearIndividuo():
return[np.random.random((3, 3))]
def crearPoblacion():
return [crearIndividuo() for i in range(Poblacion)]
def calcularFitness(individual):
error = 0
i=0
for j in x:
error += np.array(individual).dot(j)-y[i]
i += 1
error = np.linalg.norm(error,ord=1)
fitness = math.exp(-error)
return fitness
def selection_and_reproduction(population):
puntuados = [ (calcularFitness(i), i) for i in population]
puntuados = [i[1] for i in sorted(puntuados)]
population = puntuados
selected = puntuados[(len(puntuados)-pressure):]
j=0
while (j < int(len(population)-pressure)):
padre = rd.sample(selected, 2)
population[j] = 0.5*(np.array(padre[0]) + np.array(padre[1]))
j += 1
population[j] = 1.5*np.array(padre[0]) - 0.5*np.array(padre[1])
j += 1
population[j] = -0.5*np.array(padre[0]) + 1.5*np.array(padre[1])
j += 1
return population
population = crearPoblacion()
for l in range(3):
population = selection_and_reproduction(population)
print("final population: ", population)
The error occurs in the line:
puntuados = [i[1] for i in sorted(puntuados)]
I can't figure out what I m doing wrong (I am not an expert in python). Can anyone help me?
Thanks in advance.
The problem arises where the tuples share the same first element, so
sorted(puntuados)
has to compare the second elements of the two tuples to determine their relative order, at which point you encounter this exception.
You can use
sorted(graded, key=lambda x: x[0])
to solve your problem, if you only want to sort based on the first element of the tuples.

Numpy for loop gives a different result each time

First time publishing in here, here it goes:
I have two sets of data(v and t), each one has 46 values. The data is imported with "pandas" module and coverted to a numpy array in order to do the calculation.
I need to set ml_min1[45], ml_min2[45], and so on to the value "0". The problem is that each time I ran the script, the values corresponding to the position 45 of ml_min1 and ml_min2 are different. This is the piece of code that I have:
t1 = fil_copy.t1.as_matrix()
t2 = fil_copy.t2.as_matrix()
v1 = fil_copy.v1.as_matrix()
v2 = fil_copy.v2.as_matrix()
ml_min1 = np.empty(len(t1))
l_h1 = np.empty(len(t1))
ml_min2 = np.empty(len(t2))
l_h2 = np.empty(len(t2))
for i in range(0, (len(v1) - 1)):
if (i != (len(v1) - 1)) and (v1[i+1] > v1[i]):
ml_min1[i] = v1[i+1] - v1[i]
l_h1[i] = ml_min1[i] * (60/1000)
elif i == (len(v1)-1):
ml_min1[i] = 0
l_h1[i] = 0
print(i, ml_min1[i])
else:
ml_min1[i] = 0
l_h1[i] = 0
print(i, ml_min1[i])
for i in range(0, (len(v2) - 1)):
if (i != (len(v2) - 1)) and (v2[i+1] > v2[i]):
ml_min2[i] = v2[i+1] - v2[i]
l_h2[i] = ml_min2[i] * (60/1000)
elif i == (len(v2)-1):
ml_min2[i] = 0
l_h2[i] = 0
print(i, ml_min2[i])
else:
ml_min2[i] = 0
l_h2[i] = 0
print(i, ml_min2[i])
Your code as it is currently written doesn't work because the elif blocks are never hit, since range(0, x) does not include x (it stops just before getting there). The easiest way to solve this is probably just to initialize your output arrays with numpy.zeros rather than numpy.empty, since then you don't need to do anything in the elif and else blocks (you can just delete them).
That said, it's generally a design error to use loops like yours in numpy code. Instead, you should use numpy's broadcasting features to perform your mathematical operations to a whole array (or a slice of one) at once.
If I understand correctly, the following should be equivalent to what you wanted your code to do (just for one of the arrays, the other should work the same):
ml_min1 = np.zeros(len(t1)) # use zeros rather than empty, so we don't need to assign any 0s
diff = v1[1:] - v1[:-1] # find the differences between all adjacent values (using slices)
mask = diff > 0 # check which ones are positive (creates a Boolean array)
ml_min1[:-1][mask] = diff[mask] # assign with mask to a slice of the ml_min1 array
l_h1 = ml_min1 * (60/1000) # create l_h1 array with a broadcast scalar multiplication

Get pairs of positive values efficiently with numpy

I have a python function which takes in two lists, looks for pairs in the two inputs where both have positive values at the same index, and creates two output lists by appending to each one of those two positive values. I have a working function:
def get_pairs_in_first_quadrant(x_in, y_in):
"""If both x_in[i] and y_in[i] are > 0 then both will appended to the output list. If either are negative
then the pair of them will be absent from the output list.
:param x_in: A list of positive or negative floats
:param y_in: A list of positive or negative floats
:return: A list of positive floats <= in length to the inputs.
"""
x_filtered, y_filtered = [], []
for x, y in zip(x_in, y_in):
if x > 0 and y > 0:
x_filtered.append(x)
y_filtered.append(y)
return x_filtered, y_filtered
How can I make this faster using numpy?
You can do this by simply finding the indices where they are both positive:
import numpy as np
a = np.random.random(10) - .5
b = np.random.random(10) - .5
def get_pairs_in_first_quadrant(x_in, y_in):
i = np.nonzero( (x_in>0) & (y_in>0) ) # main line of interest
return x_in[i], y_in[i]
print a # [-0.18012451 -0.40924713 -0.3788772 0.3186816 0.14811581 -0.04021951 -0.21278312 -0.36762629 -0.45369899 -0.46374929]
print b # [ 0.33005969 -0.03167875 0.11387641 0.22101336 0.38412264 -0.3880842 0.08679424 0.3126209 -0.08760505 -0.40921421]
print get_pairs_in_first_quadrant(a, b) # (array([ 0.3186816 , 0.14811581]), array([ 0.22101336, 0.38412264]))
I was interested in Jaime's suggestion to just using the boolean indexing without calling nonzero so I ran some timing tests. The results are somewhat interesting since they advantage ratio is non-monotonic with the number of positive matches, but basically, at least for speed, it doesn't really matter which is used (though nonzero is usually a bit faster, and can be about twice as fast):
threshold = .6
a = np.random.random(10000) - threshold
b = np.random.random(10000) - threshold
def f1(x_in, y_in):
i = np.nonzero( (x_in>0) & (y_in>0) ) # main line of interest
return x_in[i], y_in[i]
def f2(x_in, y_in):
i = (x_in>0) & (y_in>0) # main line of interest
return x_in[i], y_in[i]
print threshold, len(f1(a,b)[0]), len(f2(a,b)[0])
print timeit("f1(a, b)", "from __main__ import a, b, f1, f2", number = 1000)
print timeit("f2(a, b)", "from __main__ import a, b, f1, f2", number = 1000)
Which gives, for different threshold values:
0.05 9086 9086
0.0815141201019
0.104746818542
0.5 2535 2535
0.0715141296387
0.153401851654
0.95 21 21
0.027126789093
0.0324990749359

Categories