I'm looking for a pythonic (1-line) way to extract a range of values from an array
Here's some sample code that will extract the array elements that are >2 and <8 from x,y data, and put them into a new array. Is there a way to accomplish this on a single line? The code below works but seems kludgier than it needs to be. (Note I'm actually working with floats in my application)
import numpy as np
x0 = np.array([0,3,9,8,3,4,5])
y0 = np.array([2,3,5,7,8,1,0])
x1 = x0[x0>2]
y1 = y0[x0>2]
x2 = x1[x1<8]
y2 = y1[x1<8]
print x2, y2
This prints
[3 3 4 5] [3 8 1 0]
Part (b) of the problem would be to extract values say 1 < x < 3 and 7 < x < 9 as well as their corresponding y values.
You can chain together boolean arrays using & for element-wise logical and and | for element-wise logical or, so that the condition 2 < x0 and x0 < 8 becomes
mask = (2 < x0) & (x0 < 8)
For example,
import numpy as np
x0 = np.array([0,3,9,8,3,4,5])
y0 = np.array([2,3,5,7,8,1,0])
mask = (2 < x0) & (x0 < 8)
x2 = x0[mask]
y2 = y0[mask]
print(x2, y2)
# (array([3, 3, 4, 5]), array([3, 8, 1, 0]))
mask2 = ((1 < x0) & (x0 < 3)) | ((7 < x0) & (x0 < 9))
x3 = x0[mask2]
y3 = y0[mask2]
print(x3, y3)
# (array([8]), array([7]))
import numpy as np
x0 = np.array([0,3,9,8,3,4,5])
y0 = np.array([2,3,5,7,8,1,0])
list( zip( *[(x,y) for x, y in zip(x0, y0) if 1<=x<=3 or 7<=x<=9] ) )
# [(3, 9, 8, 3), (3, 5, 7, 8)]
Related
The task: calculate the angle between two lines
input:
float x1, y1 (starting point)
float x2, y2 (end point)
output:
float phi (angle in deg, between the lines)
additionally:
one line is parallel to the x-axis, and phi is 0° ≤ phi < 359°
Task Picture
option 1:
import math
rad_to deg = lambda x: 180.0/math.pi * x
# start-point
x1 = float(input("x1: "))
y1 = float(input("y1: "))
# end-point
x2 = float(input("x2: "))
y2 = float(input("y2: "))
# slope of one line (parallel to the x-axis)
m1 = 0
# slope between the start-point and end-point
# special-case: division with zero
if x1 == x2:
m2 = "Not defined!"
if y2 > y1: # end-point over start-point
phi = 90
elif y2 < y1: # end-point under start-point
phi = 270
else:
m2 = (y2 - y2) / (x2 - x1)
# angle between two lines (smaller angle: 0° < phi < 90°)
angle = rad_to_deg(math.atan(abs((m1 - m2) / (1 + m1 * m2))))
if x1 < x2 and y1 < y2: # 1. quadrant
phi = sw
elif x1 > x2 and y1 < y2: # 2. quadrant
phi = 180 - sw
elif x1 > x2 and y1 > y2: # 3. quadrant
phi = 180 + sw
elif x1 < x2 and y1 > y2: # 4. quadrant
phi = 360 - sw
elif y1 == y2 and x1 > x2: # end-point left from start-point
phi = 180
elif y1 == y2 and x1 < x2 : # end-point right from start-point
phi = 0
elif x1 == x2 and y1 == y2:
phi = "Error, start-point is end-point"
print("angle phi: " + str(phi))
or should be the special-case with try/except?
try:
m2 = (y2 - y2) / (x2 - x1)
except ZeroDivisionError: # x1 == x2
m2 = "Not defined!"
if y2 > y1: # end-point over start-point
phi = 90
elif y2 < y1: # end-point under start-point
phi = 270
option 2: with vectors
for math import sqrt, acos, pi
rad_to_deg = lambda x: 180.0/pi * x
x1 = float(input("x1: "))
y1 = float(input("y1: "))
x2 = float(input("x1: "))
y2 = float(input("x2: "))
# vectors
u = [1, 0] # start-point and (a point right from the start-point)
v = [x2 - x1, y2 - y1]
# calculation
u_scalar_v = u[0] * v[0] + u[1] * v[1]
u_amount = sqrt(u[0] * u[0] + u[1] * u[1])
v_amount = sqrt(v[0] * v[0] + v[1] * v[1])
# scalar product
phi = round(rad_to_deg(acos(u_scalar_v / (u_amount * v_amount))), 5)
if y2 >= y1:
pass
else:
phi = 360 - phi
print(phi)
option 3:
is to see the start-point, the end-point and a point right from s, as a triangle and to calculate over the cosine theorem
What is the most efficient way to calculate this and how can I decide it?
Find the Angle between three points from 2D using python provides a simple solution.
import math
def getAngle(a, b, c):
ang = math.degrees(math.atan2(c[1]-b[1], c[0]-b[0]) - math.atan2(a[1]-b[1], a[0]-b[0]))
return ang + 360 if ang < 0 else ang
# starting-point
x1 = float(input("x1: "))
y1 = float(input("y1: "))
# middle-point (intersection point)
x2 = float(input("x2: "))
y2 = float(input("y2: "))
# ending point of horizontal line
x3 = x2 + 1 # i.e. horizontal offset from mid-point (x2, y2)
y3 = y2
a = (x1, y1)
b = (x2, y2)
c = (x3, y3)
angle = getAngle(a, b, c)
Example
a = (5, 0)
b = (0, 0)
c = (0, 5)
print(getAngle(a, b, c)) # result 90.0
Example 2--test with random points
from random import randrange, sample
radius = 10
points = []
for i1 in range(10):
for i2 in range(10):
points.append((randrange(-radius, radius+1), randrange(-radius, radius+1)))
x1y1 = sample(points[:50], 10)
x2y2 = sample(points[50:], 10)
x3y3 = [(x+1, y) for x, y in x2y2]
for i in range(len(x1y1)):
a, b, c = x1y1[i], x2y2[i], x3y3[i]
angle = getAngle(a, b, c)
print(i, ": ", a, b, c, '=> ', angle)
Result
0 : (10, -6) (8, -10) (9, -10) => 296.565051177078
1 : (0, -9) (-4, -3) (-3, -3) => 56.309932474020215
2 : (-6, 10) (5, 9) (6, 9) => 185.1944289077348
3 : (0, 1) (-2, 1) (-1, 1) => 0.0
4 : (2, -1) (-3, 7) (-2, 7) => 57.9946167919165
5 : (2, -3) (-10, -8) (-9, -8) => 337.3801350519596
6 : (2, -6) (-10, 5) (-9, 5) => 42.510447078000844
7 : (7, 8) (7, 3) (8, 3) => 270.0
8 : (2, -2) (-4, 4) (-3, 4) => 45.0
9 : (1, -2) (-2, 7) (-1, 7) => 71.56505117707799
Suppose we have two numpy array x1 and x2 like below:
x1 = np.array([[0,2,9,1,0]])
x2 = np.array([[7,3,0,6,8]])
Is there any operation like:
x2(operation)x1 = array([[ 0, 3, 0, 6, 0]])
i.e. if x1 or x2 is 0 at any index then make the result array's index value as zero. Otherwise, keep x2 as it is.
Use numpy.where:
x3 = np.where(x1 == 0, x1, x2)
print(x3)
Output:
[[0 3 0 6 0]]
Given that you want to keep x2 but make it zero in the case x1 is zero, just multiply x2 by the boolean of x1.
>>> x2 * x1.astype(bool)
array([[0, 3, 0, 6, 0]])
Note that if x2 is zero, the result is zero as expected.
Here is my dataframe,
df = pd.DataFrame({'Id': [102,103,104,303,305],'ExpG_Home':[1.8,1.5,1.6,1.8,2.9],
'ExpG_Away':[2.2,1.3,1.2,2.8,0.8],
'HomeG_Time':[[93, 109, 187],[169], [31, 159],[176],[16, 48, 66, 128]],
'AwayG_Time':[[90, 177],[],[],[123,136],[40]]})
First, I need to create an array y, for a given Id number, it takes values from same row (ExpG_Home & ExpG_Away).
y = [1 - (ExpG_Home + ExpG_Away), ExpG_Home, ExpG_Away]
Second, I found this much harder, for the Id used in creating y, the function below takes the corresponding lists from HomeG_Time & AwayG_Time and creates an array. Unfortunately, my function takes one row at a time. I need to do this for a large dataset.
x1 = [1,0,0]
x2 = [0,1,0]
x3 = [0,0,1]
total_timeslot = 200 # number of timeslot per game.
k = 1 # constant
#For Id=102 with ExpG_Home=2.2 and ExpG_Away=1.8
HomeG_Time = [93, 109, 187]
AwayG_Time = [90, 177]
y = np.array([1-(2.2 + 1.8)/k, 2.2/k, 1.8/k])
# output of y = [0.98 , 0.011, 0.009]
def squared_diff(x1, x2, x3, y):
ssd = []
for k in range(total_timeslot):
if k in HomeG_Time:
ssd.append(sum((x2 - y) ** 2))
elif k in AwayG_Time:
ssd.append(sum((x3 - y) ** 2))
else:
ssd.append(sum((x1 - y) ** 2))
return ssd
sum(squared_diff(x1, x2, x3, y))
Out[37]: 7.880400000000012
This output is for the first row only.
Here is the complete snippet given,
>>> import numpy as np
>>> x1 = np.array( [1,0,0] )
>>> x2 = np.array( [0,1,0] )
>>> x3 = np.array( [0,0,1] )
>>> total_timeslot = 200
>>> HomeG_Time = [93, 109, 187]
>>> AwayG_Time = [90, 177]
>>> ExpG_Home=2.2
>>> ExpG_Away=1.8
>>> y = np.array( [1 - (ExpG_Home + ExpG_Away), ExpG_Home, ExpG_Away] )
>>> def squared_diff(x1, x2, x3, y):
... ssd = []
... for k in range(total_timeslot):
... if k in HomeG_Time:
... ssd.append(sum((x2 - y) ** 2))
... elif k in AwayG_Time:
... ssd.append(sum((x3 - y) ** 2))
... else:
... ssd.append(sum((x1 - y) ** 2))
... return ssd
...
>>> sum(squared_diff(x1, x2, x3, y))
4765.599999999989
Assuming this. Calculate y as (N,3) using pandas.DataFrame.apply
>>> y = np.array( df.apply(lambda row: [1 - (row.ExpG_Home + row.ExpG_Away),
... row.ExpG_Home, row.ExpG_Away ],
... axis=1).tolist() )
>>> y.shape
(5, 3)
Now calcualte squared error for a given x
>>> def squared_diff(x, y):
... return np.sum( np.square(x - y), axis=1)
In your case, if error2 is squared_diff(x2,y) you are adding this the number of occuerences of HomeG_Time
>>> n3 = df.AwayG_Time.apply(len)
>>> n2 = df.HomeG_Time.apply(len)
>>> n1 = 200 - (n2 + n3)
The final sum of squared error is (as per your calculation)
>>> squared_diff(x1, y) * n1 + squared_diff(x2, y) * n2 + squared_diff(x3, y) * n3
0 4766.4
1 2349.4
2 2354.4
3 6411.6
4 4496.2
dtype: float64
>>>
try this,
import pandas as pd
import numpy as np
df = pd.DataFrame({'Id': [102,103,104,303,305],'ExpG_Home':[1.8,1.5,1.6,1.8,2.9],
'ExpG_Away':[2.2,1.3,1.2,2.8,0.8],
'HomeG_Time':[[93, 109, 187],[169], [31, 159],[176],[16, 48, 66, 128]],
'AwayG_Time':[[90, 177],[],[],[123,136],[40]]})
x1 = [1,0,0]
x2 = [0,1,0]
x3 = [0,0,1]
k=1
total_timeslot = 200 # number of timeslot per game.
def squared_diff(x1, x2, x3,AwayG_Time,HomeG_Time, y):
ssd = []
for k in range(total_timeslot):
if k in HomeG_Time:
ssd.append(sum((x2 - y) ** 2))
elif k in AwayG_Time:
ssd.append(sum((x3 - y) ** 2))
else:
ssd.append(sum((x1 - y) ** 2))
return ssd
s=pd.DataFrame( pd.concat([df,1-(df['ExpG_Home']+df['ExpG_Away'])/k,df['ExpG_Home']/k,df['ExpG_Away']/k],axis=1).values)
df['res']=s.apply(lambda x: sum(squared_diff(x1,x2,x3,x[0],x[3],np.array([x[5],x[6],x[7]]))),axis=1)
del s
print df
Output:
AwayG_Time ExpG_Away ExpG_Home HomeG_Time Id res
0 [90, 177] 2.2 1.8 [93, 109, 187] 102 4766.4
1 [] 1.3 1.5 [169] 103 2349.4
2 [] 1.2 1.6 [31, 159] 104 2354.4
3 [123, 136] 2.8 1.8 [176] 303 6411.6
4 [40] 0.8 2.9 [16, 48, 66, 128] 305 4496.2
def squared_diff(row):
y = np.array([1 - (row.ExpG_Home + row.ExpG_Away), row.ExpG_Home, row.ExpG_Away])
HomeG_Time = row.HomeG_Time
AwayG_Time = row.AwayG_Time
x1 = np.array([1, 0, 0])
x2 = np.array([0, 1, 0])
x3 = np.array([0, 0, 1])
total_timeslot = 200
ssd = []
for k in range(total_timeslot):
if k in HomeG_Time:
ssd.append(sum((x2 - y) ** 2))
elif k in AwayG_Time:
ssd.append(sum((x3 - y) ** 2))
else:
ssd.append(sum((x1 - y) ** 2))
return sum(ssd)
df.apply(squared_diff, axis=1)
Out[]:
0 4766.4
1 2349.4
2 2354.4
3 6411.6
4 4496.2
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have a problem simplified into the following:
Xn+1 = Xn + Yn
Yn+1= Yn + Zn
Zn+1= Zn+ Xn
I know the values of X0,Y0,Z0 to be equal to 1.
I want to tell python to find the values of X1,Y1,Z1 and then X2,Y2,Z2,...etc. Can anyone help me with that? I think I have to use a nested loop but I am not sure exactly how to go about it. Thanks!
Are we talking about something as simple as:
x, y, z = 1, 1, 1
for i in range(10):
print("X{i} = {x}, Y{i} = {y}, Z{i} = {z}".format(**locals()))
x, y, z = x + y, y + z, z + x
which doesn't seem right as the output is so uninteresting:
X0 = 1, Y0 = 1, Z0 = 1
X1 = 2, Y1 = 2, Z1 = 2
X2 = 4, Y2 = 4, Z2 = 4
X3 = 8, Y3 = 8, Z3 = 8
X4 = 16, Y4 = 16, Z4 = 16
X5 = 32, Y5 = 32, Z5 = 32
X6 = 64, Y6 = 64, Z6 = 64
X7 = 128, Y7 = 128, Z7 = 128
X8 = 256, Y8 = 256, Z8 = 256
X9 = 512, Y9 = 512, Z9 = 512
Below is the sample function to achieve it:
def solve_equation(n):
X = {0: 1}
Y = {0: 1}
Z = {0: 1}
for i in range(n):
print 'For n: ', i+1
X[i+1] = X[i] + Y[i]
Y[i+1] = Y[i] + Z[i]
Z[i+1] = Z[i] + X[i]
print 'X = ', X[i+1], ' Y = ', Y[i+1], ' Z = ', Z[i+1]
Sample run:
>>> solve_equation(3)
For n: 1
X = 2 Y = 2 Z = 2
For n: 2
X = 4 Y = 4 Z = 4
For n: 3
X = 8 Y = 8 Z = 8
You can use recursive functions. For a faster and better code you can consider using yield
def Cvalue(c,xyz , n):
if n == 0 :
res = c
else :
if xyz == 1: res = Cvalue(c,1, n-1) + Cvalue(c,2, n-1)
elif xyz == 2: res = Cvalue(c,2, n-1) + Cvalue(c,3, n-1)
elif xyz == 3: res = Cvalue(c,3, n-1) + Cvalue(c,1, n-1)
else: print("Error\n")
return res
def XYZvalues(x0, y0, z0, n):
x = Cvalue(x0,1, n)
y = Cvalue(y0,2, n)
z = Cvalue(z0,3, n)
return (x, y , z)
print(XYZvalues(1,1,1, 3))
I'm trying to figure out what is wrong with my implementation, I expect the result to be [5, 10], I don't understand how it gets [7.5, 7.5], x1 should be half of x2.
from scipy.optimize import linprog
import numpy as np
c = [-1, -1]
A_eq = np.array([
[1, 0.5],
[1, -0.5],
])
b_eq = [15, 0]
x0_bounds = (0, None)
x1_bounds = (0, None)
res = linprog(
c,
A_eq=A_eq.transpose(),
b_eq=b_eq,
bounds=(x0_bounds, x1_bounds),
options={"disp": True})
print res.x
# =>
# Optimization terminated successfully.
# Current function value: -15.000000
# Iterations: 2
# [ 7.5 7.5]
Update from the author:
As it was said matrix transposition is not needed here.
The problem was in the matrix itself, in order to get desired result, which is [5, 10], it has to be:
A_eq = np.array([
[1, 1],
[1, -0.5],
])
Per the scipy linprog docs:
Minimize: c^T * x
Subject to:
A_ub * x <= b_ub
A_eq * x == b_eq
So, you are now solving the following equations:
Minimize -x1 -x2
Subject to,*
x1 + x2 = 15 (i)
0.5 * x1 - 0.5 * x2 = 0 (ii)
Now, (ii) implies x1 = x2 (so your desired solution is infeasable), and then (i) fixes x1 = x2 = 7.5. So, the solution returned by linprog() is indeed correct. Since you are expecting a different result, maybe you should look into the way you translated your problem into code, as I think that's where you will find both the issue and the solution.
*) Since you are taking the transpose.
Your problem is:
x1 + x2 == 15
0.5 * x1 - 0.5 * x2 == 0
minimize -x1 -x2
So obviously you have x1 == x2 (second constraint), and thus x1 = x2 = 7.5 (first constraint).
Looking at your question, you probably don't want to transpose A:
res = linprog(
c,
A_eq=A_eq,
b_eq=b_eq,
bounds=(x0_bounds, x1_bounds),
options={"disp": True}
)
Why gives you the problem:
x1 + 0.5 * x2 == 15
x1 - 0.5 * x2 == 0
minimize -x1 -x2
And you get x1 = 7.5 and x2 = 15 (the only possible values).