What is the best way to implement a random event in Python?
For example:
The variable a is set to 1 with a probability of 0.8 and otherwise to 2.
I've done this until now as following:
import random
a = 0 #Initialize a to 0
prob = random.random() #ask Python for a random float between 0 and 1
if prob < 0.8:
a = 1
else:
a = 2
Is there another way for such cases?
You can use random.choices:
import random
a = random.choices([1, 2], weights=[0.8, 0.2])[0]
You can use numpy random with values and their probabilities
from numpy.random import choice
a = [0,2]
output = choice(a, 1, p=[0.8,0.2])
print(output)
Syntax: numpy.random.choice(list,k, p=None)
List: It is the original list from you have select random numbers.
k: It is the size of the returning list. i.e, the number of elements you want to select.
p: It is the probability of each element.
You could use random.choices to make it a little more pythonic.
a = random.choices([1,2], weights = [4, 1])[0]
Continuous form distribution
Continuous form distribution is explained on Wikipedia, and grants you to have the same probability of extracting each value in the interval [0, 1). That's exactly what you want to have: the probability of 80% of getting a=1 and 20% of getting a=2.
Numpy python implementation
Using numpy.random.random_sample() you can achieve this, from docs:
Results are from the “continuous uniform” distribution over the stated
interval.
import numpy as np
rand = np.random.random_sample() # sample output: 0.5320258785026352
I personally prefer to write your code like this:
import random
prob = random.random()
a = 1 if prob < 0.8 else 2
Related
I am quite new in this and I am trying to learn on my own. As I said in the title, I am trying to create a list of 100 numbers whose elements are either 50% chance of being 0's or 50% change being a number between 0 and 1. I made it like the one below. It works but it is a very tedious and not well coded program. Any hints of how to make to make it better?
import random
import numpy as np
#define a list of 100 random numbers between 0 and 1
randomlist = []
for i in range(0,100):
n = random.uniform(0,1)
randomlist.append(n)
print(randomlist)
#create a list of 100 numbers of 0's and 1's
def random_binary_string(length):
sample_values = '01' # pool of strings
result_str = ''.join((random.choice(sample_values) for i in range(length)))
return (result_str)
l=100
x=random_binary_string(l)
x1=np.array(list(map(int, x)))
print(x1)
#combine both lists. Keep value if of the binary list if it is equal to zero. Else, substitute it by the value of randomlist
#corresponding to the index position
finalist=[]
for i in range(len(x1)):
if x1[i]==0:
finalist.append(x1[i])
else:
finalist.append(randomlist[i])
print(finalist)
Thanks a lot!
You can simplify your code by nesting the two conditions. This avoids the need to keep two separate lists in memory and then merge them at the end.
randomlist = []
for i in range(0,100):
if random.choice((0, 1)) == 1:
randomlist.append(random.uniform(0,1))
else:
randomlist.append(0)
This is simple and succinct enough that you can refactor it to a single list comprehension. This is more compact but somewhat less legible.
randomlist = [random.uniform(0,1) if random.choice((0, 1)) else 0 for i in range(0,100)]
Here, we also shorten the code slightly by exploiting the fact that 0 is falsey and 1 is truthy in Python; i.e. they evaluate to False and True, respectively, in a boolean context. So if random.choice((0, 1)) == 1 can be abbreviated to simply if random.choice((0, 1)).
Somewhat obscurely, you can further simplify this (in the sense, use less code) by observing that the expression B if not A else A can be short-circuited into the expression A and B. This is not very obvious if you are not very familiar with boolean logic, but I think you can work it out on paper.
randomlist = [random.choice((0, 1)) and random.uniform(0,1) for i in range(0,100)]
Demo: https://ideone.com/uGHS2Y
You could try doing something like this:
import random
def create_random_list():
random_list = list()
for _ in range(100):
if random.choice((True, False)):
random_list.append(0)
else:
random_list.append(random.uniform(0, 1))
return random_list
randomly_generated_list = create_random_list()
print(len(randomly_generated_list), randomly_generated_list)
# 100 [x_0,...,x_99]
I propose this method:
first generate a random list of 'A', and 'B' with random.choice. 50% 'A' and 50% 'B'
then replace 'A' by random number between 0 and 1
and replace 'B' by 0
code here:
import random
ll = [ random.choice(['A', 'B']) for x in range(200)]
print(ll, len(ll))
for i in range(len(ll)):
if ll[i] == 'A':
ll[i]=random.random()
else:
ll[i]=0
print(ll, len(ll))
Shorter code here:
import random
ll = [ random.choice([0, random.random()]) for x in range(200)]
print(ll, len(ll), ll.count(0))
Since you are using Numpy, I probably would do what follow:
Create the array of num_el elements using random.uniform
Consider if the problem of the excluded upper bound: [low, high)
Create a boolean matrix with probability p=0.5 between true and false: random.choice
Use the matrix to set some elements of the array to zero, by
That's the code:
num_el = 10
p = 0.5
res = np.random.uniform(0., 1., size=(1, num_el))
bool_mat = np.random.choice(a=[False, True], size=(1, num_el), p=[p, 1-p])
res[bool_mat] = 0.
res
# array([[0. , 0.51213168, 0. , 0.68230528, 0.5287728 ,
# 0.9072587 , 0. , 0.43078057, 0.89735872, 0. ]])
The approach to use depends on whether your objective is to get exactly half of the outcomes to be zeroes, or have the expected number of zeros be half the total. It wasn't clear from your question which way you viewed the problem, so I've implemented both approaches as functions.
If you want a deterministic fixed proportion of zeroes/non-zeroes, the first function in the code below will do the trick. It creates a list with the desired number of zeros and non-zeros, and then uses shuffling (which I timed to be faster than sampling). If you want exactly half, then obviously the argument n has to be even.
If your goal is a probabilistic 50% zeroes, use the second function.
import random
# Exactly floor(n / 2) outcomes are zeros, i.e., exactly half when n is even.
# This version is trivial to modify to give any desired proportion of zeros.
def make_rand_list_v1(n = 100):
m = n // 2
n -= m
ary = [random.random() for _ in range(n)] + [0] * m
random.shuffle(ary)
return ary
# Each outcome has probability 0.5 of being zero
def make_rand_list_v2(n = 100):
return [random.getrandbits(1) and random.uniform(0, 1) for _ in range(n)]
I am trying to create a random list of 1 million numbers ranging between 1-100. I have found how to create a single random number but not to create a list of them. In addition, I would prefer to use the numpy uniform function but the solution doesn't have to use this.
For a big array, you'd better use numpy
import numpy as np
X = np.random.randint(1, 101, size=10**6)
Try this simpler one :
from random import randint
my_list = [randint(1, 100) for i in range(1000000)]
The function
np.random.rand(Dim1,Dim2)
will create an array for the given dimensions with values selected from the uniform distribution [0,1]. You can modify this to randomArray = 1 + 99 * np.random.rand(Dim1,Dim2) to have the array with numbers ranging between 1-100.
from random import *
mylist = []
for i in range(0, 100):
f = randint(0, 100)
mylist.insert(i, f)
This code will give 100 random elements to your empty list.
use this as simple as that...don't make your program more complex in code and in efficiency too..
from random import randint
l=[randint(1,100) for i in range(0,1000000)]
I am trying to create a loop in Python with numpy that will give me a variable "times" with 5 numbers generated randomly between 0 and 20. However, I want there to be one condition: that none of the differences between two adjacent elements in that list are less than 1. What is the best way to achieve this? I tried with the last two lines of code, but this is most likely wrong.
for j in range(1,6):
times = np.random.rand(1, 5) * 20
times.sort()
print times
da = np.diff(times)
if da.sum < 1: break
For instance, for one iteration, this would not be good:
4.25230915 4.36463992 10.35915732 12.39446368 18.46893283
But something like this would be perfect:
1.47166904 6.85610453 10.81431629 12.10176092 15.53569052
Since you are using numpy, you might as well use the built-in functions for uniform random numbers.
def uniform_min_range(a, b, n, min_dist):
while True:
x = np.random.uniform(a, b, size=n)
np.sort(x)
if np.all(np.diff(x) >= min_dist):
return x
It uses the same trial-and-error approach as the previous answer, so depending on the parameters the time to find a solution can be large.
Use a hit and miss approach to guarantee uniform distribution. Here is a straight-Python implementation which should be tweakable for numpy:
import random
def randSpacedPoints(n,a,b,minDist):
#draws n random numbers in [a,b]
# with property that their distance apart is >= minDist
#uses a hit-miss approach
while True:
nums = [a + (b-a)*random.random() for i in range(n)]
nums.sort()
if all(nums[i] + minDist < nums[i+1] for i in range(n-1)):
return nums
For example,
>>> randSpacedPoints(5,0,20,1)
[0.6681336968970486, 6.882374558960349, 9.73325447748434, 11.774594560239493, 16.009157676493903]
If there is no feasible solution this will hang in an infinite loop (so you might want to add a safety parameter which controls the number of trials).
I am trying to find a function (in Python, ideally) that will tell me how 'similar' a number is to a given list of numbers. The end goal is to find out which list a given number is more likely to be a member of.
For example, take the two lists:
a = [5,4,8,3,6,4,7,2]
b = [9,5,14,10,11,18,9]
the function should take a new number, and tell me how similar it is to a given list. For example lets assume a hypothetical 'isSimilar' function will return a percentage chance that a number could be a member of a provided list:
# 5 looks pretty similar to list 'a' but not list 'b'.
>>> print isSimilar(a,5)
.9
>>> print isSimilar(b,5)
.5
# 15 looks more similar to list 'b'
>>> print isSimilar(a,15)
.4
>>> print isSimilar(b,15)
.8
# 10 looks like it has roughly the same chance to be in both lists
>>> print isSimilar(a,10)
.41
>>> print isSimilar(b,10)
.5
Ideally this hypothetical function would take the standard deviation of the lists into consideration. So, for example, in the following two lists:
a = [5,6,4,5]
b = [1,9,2,8]
the number '5' is more 'similar' to list 'a' than 'b' because the std deviation of the numbers in 'a' is much smaller.
Any help pointing me in the right direction would be much appreciated.
How about using an estimated pdf for both sets?
def get_most_likely_distribution_membership(value,d1,d2):
nparam_density1 = stats.kde.gaussian_kde(d1) # can use a different kernel
nparam_density2 = stats.kde.gaussian_kde(d2)
x = np.linspace(-20, 30, 200) # maybe pre-define a range
nparam_density1 = nparam_density1(x)
nparam_density2 = nparam_density2(x)
assert d1!=d2
if nparam_density1[np.where(abs(x-(value))==min(abs(x-(value))))].tolist() > nparam_density2[np.where(abs(x-(value))==min(abs(x-(value))))].tolist():
return 1
else:
return 2
Essentially, we're saying that if a single value the is more probable in a distribution, it's probably from that distribution.
Example:
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
a = [5,4,8,3,6,4,7,2] # 1
b = [9,5,14,10,11,18,9] # 2
print(get_most_likely_distribution_membership(6,a,b))
print(get_most_likely_distribution_membership(10,a,b))
1 and 2, respectively.
Maybe something like this:
def isSimilar(a_list, member):
m_count = a_list.count(member)
return m_count / len(a_list)
Or perhaps using sets:
set(a_list).intersection( set(b_list))
which will return the intersection of the two lists, you could then count the resultant list and do some other maths.
Also consider using difflib if you are working with strings/sequences/etc: https://docs.python.org/2/library/difflib.html
Docs on list.count():
https://docs.python.org/2/tutorial/datastructures.html
So, I'm not exactly sure about the percentage thing. But, figuring out which list the number is more likely to belong to shouldn't be too difficult. I would just calculate the average difference between the number and all the numbers in the list. The closer the average distance is to 0, the more likely it is to be in the list.
def whichList(self, list1, list2, someNumber):
if self.averageDistance(someNumber,list1) < self.averageDistance(someNumber, list2):
print "list 1"
else:
print "list 2"
def averageDifference(self, someNumber,myList):
sum = 0
for num in myList:
sum = sum + math.fabs(num-someNumber)
return sum/len(myList)
Any 'percentage' would be subjective, but you could still use subjective numbers to rank. This approximates the list as normal distributions and samples from them to see the likelihood of drawing the number (in a discrete bin around it).
import numpy as np
from scipy.stats import norm
def isSimilar(x, A, N=10000):
M, S = np.mean(A), np.std(A)
test = lambda: x - 0.5 <= norm.rvs(loc=M, scale=S) <= x + 0.5
count = sum(test() for _ in xrange(N))
return 1. * count / N
def most_similar(x, *args):
scores = [(A, isSimilar(x, A)) for i, A in enumerate(args)]
sorted_scores = sorted(scores, key=lambda (i, s): s, reverse=True)
return sorted_scores[0][0]
A = [4,5,5,6]
B = [1,2,8,9]
C = [5,4,8,3,6,4,7,2]
most_similar(5, A, B, C) # returns [4,5,5,6]
how do I define decimal range between 0 to 1 in python? Range() function in python returns only int values. I have some variables in my code whose number varies from 0 to 1. I am confused on how do I put that in the code. Thank you
I would add more to my question. There is no step or any increment value that would generate the decimal values. I have to use a variable which could have a value from 0 to 1. It can be any value. But the program should know its boundary that it has a range from 0 to 1. I hope I made myself clear. Thank you
http://docs.python.org/library/random.html
If you are looking for a list of random numbers between 0 and 1, I think you may have a good use of the random module
>>> import random
>>> [random.random() for _ in range(0, 10)]
[0.9445162222544106, 0.17063032908425135, 0.20110591438189673,
0.8392299590767177, 0.2841838551284578, 0.48562600723583027,
0.15468445000916797, 0.4314435745393854, 0.11913358976315869,
0.6793348370697525]
for i in range(100):
i /= 100.0
print i
Also, take a look at decimal.
def float_range(start, end, increment):
int_start = int(start / increment)
int_end = int(end / increment)
for i in range(int_start, int_end):
yield i * increment
This will output decimal number between 0 to 1 with step size 0.1
import numpy as np
mylist = np.arange(0, 1, 0.1)
It seems like list comprehensions would be fairly useful here.
mylist = [x / n for x in range(n)]
Something like that? My Python's rusty.
>>> from decimal import Decimal
>>> [Decimal(x)/10 for x in xrange(11)]
[Decimal("0"), Decimal("0.1"), Decimal("0.2"), Decimal("0.3"), Decimal("0.4"), Decimal("0.5"), Decimal("0.6"), Decimal("0.7"), Decimal("0.8"), Decimal("0.9"), Decimal("1")]
Edit, given comment on Mark Random's answer:
If you really don't want a smoothly incrementing range, but rather a random number between 0 and 1, use random.random().