I had come across the following code while reading up about RL. The probs vector contains the probabilities of each action to be taken. And I believe the given loop tries to choose an action randomly from the given distribution. Why/How does this work?
a = 0
rand_select = np.random.rand()
while True:
rand_select -= probs[a]
if rand_select < 0 or a + 1 == n_actions:
break
a += 1
actions = a
After going through similar code, I realised that "actions" contains the final action to be taken.
You can view the probabilities as a distribution of contiguous parts on the line from 0.0 to 1.0.
if we have A: 0.2, B: 0.3, C: 0.5 to the line could be
0.0 --A--> 0.2
0.2 --B--> 0.5
0.5 --C--> 1.0
And in total 1.0.
The algorithm is choosing a random location between 0.0->1.0 and finds out where it "landed" (A, B or C) by sequentially ruling out parts.
Suppose we draw 0.73, We can "visualize" it like this (selection marked with *):
0.0 ---------------------------> 1.0
*
0.0 --A--> 0.2 --B--> 0.5 --C--> 1.0
0.73 - 0.2 > 0 so we reduce 0.2 (=0.53) and are left with:
0.2 --B--> 0.5
0.5 --C--> 1.0
0.53 - 0.3 > 0 so we reduce 0.5 (=0.23) and are left with:
0.5 --C--> 1.0
0.23 - 0.5 < 0 so we know the part we drew was C.
The selection distributes the same as the probabilities and the algorithm is O(n) where n is the number of probabilities.
Related
I have this table that in which I am comparing list of articles (Article_body) with 4 baseline articles using cosine similarity:
Article_body
articleScores1
articleScores2
articleScores3
articleScores4
articleScores5
a*****
0.6
0.2
0.7
0.9
0.2
a*****
0.3
0.8
0.1
0.5
0.1
I want to add a column that gives which column has the largest cosine similarity out of 5, condition it should be at least 0.5. If none of CosineSim(i)
Article_body
articleScores1
articleScores2
articleScores3
articleScores4
Most_similar_to
a*****
0.6
0.2
0.7
0.9
CosineSim4
a*****
0.3
0.8
0.1
0.5
CosineSim2
a******
0.1
0.2
0.3
0.4
False
I am using this code to achieve this:
cos_cols = [f"articleScores{i}" for i in range(1, 6)]
def n_lar(text):
if (df[cos_cols].idxmax(axis=1)) <0.5:
return False
else:
df['Max'] = (df[cos_cols].idxmax(axis=1))
df['Most_similar_to'] = df.apply(n_lar)
However, I am getting this error:
TypeError: '<' not supported between instances of 'str' and 'float'
How can I resolve this?
edit:
I have this table that in which I am comparing list of articles (Article_body) with 4 baseline articles using cosine similarity:
I want to add a column that gives which column has the largest cosine similarity out of 5, condition it should be at least 0.5. If none of CosineSim(i) is atleast 0.5 then return False as in the table 2
(df.iloc[:, 1:-1]
.astype('float')
.apply(
lambda x: ('CosineSim' + x.idxmax()[-1]) if x.max() >= 0.5 else False
, axis=1)
)
output:
0 CosineSim4
1 CosineSim2
2 False
make result to Most_similar_to column
I am using pandas to get subgroup averages, and the basics work fine. For instance,
d = np.array([[1,4],[1,1],[0,1],[1,1]])
m = d.mean(axis=1)
p = pd.DataFrame(m,index='A1,A2,B1,B2'.split(','),columns=['Obs'])
pprint(p)
x = p.groupby([v[0] for v in p.index])
pprint(x.mean('Obs'))
x = p.groupby([v[1] for v in p.index])
pprint(x.mean('Obs'))
YIELDS:
Obs
A1 2.5
A2 1.0
B1 0.5
B2 1.0
Obs
A 1.75. <<<< 1.75 is (2.5 + 1.0) / 2
B 0.75
Obs
1 1.5
2 1.0
But, I also need to know how much A and B (1 and 2) deviate from their common mean. That is, I'd like to have tables like:
Obs Dev
A 1.75 0.50 <<< deviation of the Obs average, i.e., 1.75 - 1.25
B 0.75 -0.50 <<< 0.75 - 1.25 = -0.50
Obs Dev
1 1.5 0.25
2 1.0 -0.25
I can do this using loc, apply etc - but this seems silly. Can anyone think of an elegant way to do this using groupby or something similar?
Aggregate the means, then compute the difference to the mean of means:
(p.groupby(p.index.str[0])
.agg(Obs=('Obs', 'mean'))
.assign(Dev=lambda d: d['Obs']-d['Obs'].mean())
)
Or, in case of a variable number of items if you want the difference to the overall mean (not the mean of means!):
(p.groupby(p.index.str[0])
.agg(Obs=('Obs', 'mean'))
.assign(Dev=lambda d: d['Obs']-p['Obs'].mean()) # notice the p (not d)
)
output:
Obs Dev
A 1.75 0.5
B 0.75 -0.5
In run the follow code:
k = 0
while k <= 1:
print(k)
k += 0.1
And get result:
0
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
However, the expected output is
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0
How to make the result of python output same as in math?
Incrementing by a step size that's not exactly 0.1 (since that can't be represented as fixed point binary number) will continually increase your error. The float that the literal 0.1 gets translated to is not exactly 0.1. Computing the nearest binary approximation of the correct fraction is a better way to go:
k = 0
while k <= 10:
print(k / 10)
k += 1
k / 10 will not be an exact representation of the numbers you want except for 0.0, 0.5, 1.0, but since it will be the closest available float, it will print correctly.
As an aside, switching to integers allows you to rewrite your loop more idiomatically as
for k in range(11):
print(k / 10)
Just use round method with a precision of 1:
In [1217]: k = 0
...: while k<=1:
...: print(round(k,1))
...: k += 0.1
...:
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0
You can do with format() function as below:
k = 0
while k<=1:
print('{0:.2g}'.format(k))
k += 0.1
An alternative to your code is initialize a variable i with 0 and increment it in each loop. You then set k = 0.1 * i
You thereby avoid cumulative rounding errors.
Was wondering if there is a better way to get the probability of a 2D numpy array. Maybe using some of numpy's built in functions.
For simplicity, say we have an example array:
[['apple','pie'],
['apple','juice'],
['orange','pie'],
['strawberry','cream'],
['strawberry','candy']]
Would like to get the probability such as:
['apple' 'juice'] --> 0.4 * 0.5 = 0.2
['apple' 'pie'] --> 0.4 * 0.5 = 0.2
['orange' 'pie'] --> 0.2 * 1.0 = 0.2
['strawberry' 'candy'] --> 0.4 * 0.5 = 0.2
['strawberry' 'cream'] --> 0.4 * 0.5 = 0.2
Where 'juice' as the second word has a probabiliy of 0.2. Since apple has probability of 2/5 * 1/2 (for juice).
On the other hand, 'pie' as a second word, has a probability of 0.4. The combination of the probability from 'apple' and 'orange'.
The way I approached the problem was adding 3 new columns to the array, for the probability of 1st column, 2nd column, and the final probability. Group the array per 1st column, then per 2nd column and update the probability accordingly.
Below is my code:
a = np.array([['apple','pie'],['apple','juice'],['orange','pie'],['strawberry','cream'],['strawberry','candy']])
ans = []
unique, counts = np.unique(a.T[0], return_counts=True) ## TRANSPOSE a, AND GET unique
myCounter = zip(unique,counts)
num_rows = sum(counts)
a = np.c_[a,np.zeros(num_rows),np.zeros(num_rows),np.zeros(num_rows)] ## ADD 3 COLUMNS to a
groups = []
## GATHER GROUPS BASE ON COLUMN 0
for _unique, _count in myCounter:
index = a[:,0] == _unique ## WHERE COLUMN 0 MATCH _unique
curr_a = a[index]
for j in range(len(curr_a)):
curr_a[j][2] = _count/num_rows
groups.append(curr_a)
## GATHER UNIQUENESS FROM COLUMN 1, PER GROUP
for g in groups:
unique, counts = np.unique(g.T[1], return_counts=True)
myCounter = zip(unique, counts)
num_rows = sum(counts)
for _unique, _count in myCounter:
index = g[:, 1] == _unique
curr_g = g[index]
for j in range(len(curr_g)):
curr_g[j][3] = _count / num_rows
curr_g[j][4] = float(curr_g[j][2]) * float(curr_g[j][3]) ## COMPUTE FINAL PROBABILITY
ans.append(curr_g[j])
for an in ans:
print(an)
Outputs:
['apple' 'juice' '0.4' '0.5' '0.2']
['apple' 'pie' '0.4' '0.5' '0.2']
['orange' 'pie' '0.2' '1.0' '0.2']
['strawberry' 'candy' '0.4' '0.5' '0.2']
['strawberry' 'cream' '0.4' '0.5' '0.2']
Was wondering if there is a better short/faster way of doing this using numpy or other means. Adding columns is not necessary, this was just my way of doing it. Other approach will be acceptable.
Based on the definition of probability distribution you have given, you can use pandas to do the same i.e
import pandas as pd
a = np.array([['apple','pie'],['apple','juice'],['orange','pie'],['strawberry','cream'],['strawberry','candy']])
df = pd.DataFrame(a)
# Find the frequency of first word and divide by the total number of rows
df[2]=df[0].map(df[0].value_counts())/df.shape[0]
# Divide 1 by the total repetion
df[3]=1/(df[0].map(df[0].value_counts()))
# Multiply the probabilities
df[4]= df[2]*df[3]
Output:
0 1 2 3 4
0 apple pie 0.4 0.5 0.2
1 apple juice 0.4 0.5 0.2
2 orange pie 0.2 1.0 0.2
3 strawberry cream 0.4 0.5 0.2
4 strawberry candy 0.4 0.5 0.2
If you want that in the form of list you can use df.values.tolist()
If you dont want the columns then
df = pd.DataFrame(a)
df[2]=((df[0].map(df[0].value_counts())/df.shape[0]) * (1/(df[0].map(df[0].value_counts()))))
Output:
0 1 2
0 apple pie 0.2
1 apple juice 0.2
2 orange pie 0.2
3 strawberry cream 0.2
4 strawberry candy 0.2
For combined probablity print(df.groupby(1)[2].sum())
candy 0.2
cream 0.2
juice 0.2
pie 0.4
I try to calculate morphology erosion using FFT Convolution. I know erosion is dual operation to dilation. The first problem is I cannot use 0 as background as usualy I do. So I biased my values. Lets 0.1 value denotes background and 1.0 denotes foreground. Inverting background to foreground and perform FFT convolution with structure element (using scipy.signal.fftconvolve) I obtained result which I cannot interpret further. I know I should threshold the solution somehow and inverse again. How to do?
My 2D signal A:
1 1 0 1 1
1 1 1 1 1
0 1 1 1 0
1 1 1 1 1
1 1 0 1 1
Stucture element B:
0 1 0
1 1 1
0 1 0
Erode(A,B):
0 0 0 0 0
0 1 0 1 0
0 0 1 0 0
0 1 0 1 0
0 0 0 0 0
Using FFT Convolution, inv(A):
0.1 0.1 1.0 0.1 0.1
0.1 0.1 0.1 0.1 0.1
1.0 0.1 0.1 0.1 1.0
0.1 0.1 0.1 0.1 0.1
0.1 0.1 1.0 0.1 0.1
and B:
0.1 1.0 0.1
1.0 1.0 1.0
0.1 1.0 0.1
The result as below:
0.31 1.32 1.32 1.32 0.31
1.32 0.72 1.44 0.72 1.32
1.32 1.44 0.54 1.44 1.32
1.32 0.72 1.44 0.72 1.32
0.31 1.32 1.32 1.32 0.31
What next? normalize/threshold then inverse?
Best regards
My answer arrives really late, but I still give it.
The Erode(A,B) is wrong. This should be the answer:
1 0 0 0 1
0 1 0 1 0
0 0 1 0 0
0 1 0 1 0
1 0 0 0 1
Erosion/dilation are rank operations and more particularly min/max operations, but definitely not convolutions. Thus, you cannot perform them using FFT.
After performing the convolution by multiplying in frequency-space and inverse-transforming back into real space, you must then threshold the result above a certain value. According to the white paper Dilation and Erosion of Gray Images with Spherical Masks by J. Kukal, D. Majerova, A. Prochazka, that threshold is >0.5 for dilation, and for erosion it's >m-0.5 where m the structure element's volume (the number of 1s in B, 5 in this case).
In brief: This code will give you the expected result.
from scipy.signal import fftconvolve
import numpy as np
def erode(A,B):
thresh = np.count_nonzero(B)-0.5
return fftconvolve(A,B,'same') > thresh
This will work in any dimension and reproduce exactly the results of scipy.ndimage.binary_erosion - at least, I've tested it in 2D and 3D.
As for the other answer posted here that disputes the expected result of an erosion: it depends on the boundary condition. Both scipy.ndimage.binary_erosion and the custom erode(A,B) function written here assume that erosion may occur from all edges of the input A - i.e. A is padded out with 0s before the erosion. If you don't like this boundary condition - if you think, for example, that it should be treated as a reflected boundary condition - then you should consider padding the array yourself using e.g. np.pad(A,np.shape(B)[0],'reflect'). Then you'd need to un-pad the result afterwards.
I was light on details here because I wrote a more comprehensive answer on using frequency-space convolution to perform both erosion and dilation over at another question you asked, but I thought it was worthwhile to have a conclusive answer posted here for anyone seeking it.