Get random slice/fraction of a list in python - python

Imagine we have the following list:
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Now we want a random fraction of the previous list, i.e., a random sublist of length len(l) * frac. Therefore, if frac=0.2 the output list should have length 2 (0.2 x 10).
The following results are expected:
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l_new = sample(list=l, frac=0.3) # [4, 6, 8]
l_new = sample(list=l, frac=0.6) # [0, 1, 4, 6, 7, 8]
How can I achieve this behaviour? I have looked at random.sample, however it works by supplying a number of elements rather than a fraction of elements.

An alternative is to use random.sample with k = int(len(l) * frac), like so:
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
frac = 0.3
l = random.sample(l, int(len(l) * frac))
print(l)
>>> [4, 7, 9]
Using it as a function:
# Randomly Sample a fraction of elements from a list
def random_sample(l, frac=0.5):
return random.sample(l, int(len(l) * frac))

Related

Creating a New List Using a Percent Amount of a Pre-existing One - Python

Essentially, I have to take a pre-existing list and a percentage and return a new list with the given percentage of items from the first list in a new list. I have what follows:
def select_stop_words(percent, list):
possible_stop_words = []
l = len(list)
new_words_list = l//(percent/100)
x = int(new_words_list - 1)
possible_stop_words = [:x]
return possible_stop_words
But this always yields the same results as the first. Help??
You might want to multiply l to percent / 100:
def select_stop_words(percent, lst):
return lst[:len(lst) * percent // 100]
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(select_stop_words(50, lst)) # [1, 2, 3, 4, 5]
print(select_stop_words(20, lst)) # [1, 2]
print(select_stop_words(99, lst)) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(select_stop_words(100, lst)) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

How to get the unselected population in python random module

So, I know I can get a random list from a population using the random module,
l = [0, 1, 2, 3, 4, 8 ,9]
print(random.sample(l, 3))
# [1, 3, 2]
But, how do I get the list of the unselected ones? Do, I need to remove them manually from the list? Or, is there a method to get them too?
Edit: The list l from example doesn't contain the same items multiple times, but when it does I wouldn't want it removed more than it's selected as sample.
l = [0, 1, 2, 3, 4, 8 ,9]
s1 = set(random.sample(l, 3))
s2 = set(l).difference(s1)
>>> s1
{0, 3, 8}
>>> s2
{1, 2, 4, 9}
Update: same items multiple times
You can shuffle your list first and partition your population after in two:
l = [7, 4, 5, 4, 5, 9, 8, 6, 6, 6, 9, 8, 6, 3, 8]
pop = l[:]
random.shuffle(pop)
pop1, pop2 = pop[:3], pop[3:]
>>> pop1
[8, 4, 9]
>>> pop2
[7, 6, 8, 6, 5, 6, 9, 6, 5, 8, 4, 3]
Because your list can contain multiple same items, you can change to the approach below:
import random
l = [0, 1, 2, 3, 4, 8 ,9]
random.shuffle(l)
selected = l[:3]
unselected = l[3:]
print(selected)
# [4, 0, 1]
print(unselected)
# [8, 2, 3, 9]
If you want to keep track of duplicates, you could count the items of each type and compare the population count to the sample count.
If you don't care about the order of items in the population, you could do it like this:
from collections import Counter
import random
population = [1, 1, 2, 2, 9, 7, 9]
sample = random.sample(population, 3)
pop_count = Counter(population)
samp_count = Counter(sample)
unsampled = [
k
for k in pop_count
for i in range(pop_count[k] - samp_count[k])
]
If you care about the order in the population, you could do something like this:
check = sample.copy()
unsampled = []
for val in population:
if val in check:
check.remove(val)
else:
unsampled.append(val)
Or there's this weird list comprehension (not recommended):
check = sample.copy()
unsampled = [
x
for x in population
if x not in check or check.remove(x)
]
The if clause here uses two tricks:
both parts of the test will be Falseish if x is not in check (list.remove() always returns None), and
remove() will only be called if the first part fails, i.e., if x is in check.
Basically, if (and only if) x is in check, it will bomb through and check the next condition, which will also be False (None), but will have the side effect of removing one copy of x from check.
You can do with:
import random
l = [0, 1, 2, 3, 4, 8 ,9]
rand = random.sample(l, 3)
rest = list(set(l) - set(rand))
print(f"initial list: {l}")
print(f"random list: {rand}")
print (f"rest list: {rest}")
Result:
initial list: [0, 1, 2, 3, 4, 8, 9]
random list: [2, 9, 0]
rest list: [8, 1, 3, 4]

'List index out of range' while reversing list

The problem is regarding reversing a list A of size N in groups of K. For example if A = [1,2,3,4,5], k = 3
Output = [3,2,1,5,4]
The error I get, when I run this is List Index out of range on line 4.
def reverseInGroups(A,N,K):
arr1 = []
for i in range(K):
arr1.append(A[(N-i)%K]) #line 4
for j in range(N-K):
arr1.append(A[N-j-1])
return arr1
This will implement what you are trying to achieve:
def reverseInGroups(A,K):
N = len(A)
arr1 = []
for i in range(0, N, K):
arr1.extend(A[i : i+K][::-1])
return arr1
print(reverseInGroups([1,2,3,4,5], 3))
Interestingly, the code in the question actually works in the example case, but it is not general. The case where it works is where N = 2*K - 1 (although where it does not work, the elements are in the wrong order rather than an IndexError).
Cant seem to reproduce your 'List index out of range' error, but your logic is faulty:
reverseInGroups(A,N,K):
arr1 = []
for i in range(K):
arr1.append(A[(N-i)%K]) #line 4
for j in range(N-K):
arr1.append(A[N-j-1])
return arr1
print(reverseInGroups([1,2,3,4,5],5, 3)) # works, others get wrong result
print(reverseInGroups([1,2,3,4,5,6],6, 3)) # wrong result: [1, 3, 2, 6, 5, 4]
prints:
[3, 2, 1, 5, 4] # correct
[1, 3, 2, 6, 5, 4] # wrong
You fix this and make this smaller by packing it into a list comprehension:
def revv(L,k):
return [w for i in (L[s:s+k][::-1] for s in range(0,len(L),k)) for w in i]
for gr in range(2,8):
print(gr, revv([1,2,3,4,5,6,7,8,9,10,11],gr))
to get:
2 [2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 11]
3 [3, 2, 1, 6, 5, 4, 9, 8, 7, 11, 10]
4 [4, 3, 2, 1, 8, 7, 6, 5, 11, 10, 9]
5 [5, 4, 3, 2, 1, 10, 9, 8, 7, 6, 11]
6 [6, 5, 4, 3, 2, 1, 11, 10, 9, 8, 7]
7 [7, 6, 5, 4, 3, 2, 1, 11, 10, 9, 8]
You can also try with this:
def reverse(l, n):
result = []
for i in range(0, len(l)-1, n):
for item in reversed(l[i:i+n]):
result.append(item)
for item in reversed(l[i+n:]):
result.append(item)
return result
You can reverse the array upto index K and reverse the remaining part and add these both arrays.
def reverseInGroups(A,N,K):
return A[:K][::-1]+A[K:][::-1]
A = [1,2,3,4,5]
N = 5
K = 3
res = reverseInGroups(A,N,K)
print(res)

Python array segmented in ranges

I have the following array [1, 4, 7, 9, 2, 10, 5, 8] and I need to separate the array in 3 different arrays: one for values between 0 and 3, anther for 3 to 6 and anther for 6 and 25.The result must be something like that:
array1 = [1, 2]
array2 = [4, 5]
array3 = [7, 9, 10, 8]
Any idea about how to do it simple?
First, define your "pole" numbers
Second, generate your intervals from those "pole" numbers
Third, define as many lists as there are intervals.
Then for each interval, scan the list and appends items in the relevant list if they belong to the interval
code:
source = [1, 4, 7, 9, 2, 10, 5, 8]
poles = (0,3,6,25)
intervals = [(poles[i],poles[i+1]) for i in range(len(poles)-1)]
# will generate: intervals = [(0,3),(3,6),(6,25)]
output = [list() for _ in range(len(intervals))]
for out,(start,stop) in zip(output,intervals):
for s in source:
if start <= s <stop:
out.append(s)
print(output)
result:
[[1, 2], [4, 5], [7, 9, 10, 8]]
This solution has the advantage of being adaptable to more than 3 lists/intervals by adding more "pole" numbers.
EDIT: There's a nice & fast solution (O(log(N)*N)) if the output lists order don't matter:
first sort the input list
then generate the sliced sub-lists using bisect which returns insertion position of the provided numbers (left & right)
like this:
import bisect
source = sorted([1, 4, 7, 9, 2, 10, 5, 8])
poles = (0,3,6,25)
output = [source[bisect.bisect_left(source,poles[i]):bisect.bisect_right(source,poles[i+1])] for i in range(len(poles)-1)]
print(output)
result:
[[1, 2], [4, 5], [7, 8, 9, 10]]
You can do that in a very simple way using a combination of a for loop and range functions:
lists = ([], [], [])
for element in [1, 4, 7, 9, 2, 10, 5, 8]:
if element in range(0, 3):
lists[0].append(element)
elif element in range(3, 6):
lists[1].append(element)
elif element in range(6, 25):
lists[2].append(element)
array1, array2, array3 = lists
"One-line" solution using set.intersection(*others) and range(start, stop[, step]) functions:
l = [1, 4, 7, 9, 2, 10, 5, 8]
l1, l2, l3 = (list(set(l).intersection(range(3))), list(set(l).intersection(range(3,6))), list(set(l).intersection(range(6,25))))
print(l1)
print(l2)
print(l3)
The output:
[1, 2]
[4, 5]
[8, 9, 10, 7]
https://docs.python.org/3/library/stdtypes.html?highlight=intersection#set.intersection

python random sequence from list

If I had a list that ranged from 0 - 9 for example. How would I use the random.seed function to get a random selection from that range of numbers? Also how I define the length of the results.
import random
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = 10
random.seed(a)
length = 4
# somehow generate random l using the random.seed() and the length.
random_l = [2, 6, 1, 8]
Use random.sample. It works on any sequence:
>>> random.sample([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4)
[4, 2, 9, 0]
>>> random.sample('even strings work', 4)
['n', 't', ' ', 'r']
As with all functions within the random module, you can define the seed just as you normally would:
>>> import random
>>> lst = list(range(10))
>>> random.seed('just some random seed') # set the seed
>>> random.sample(lst, 4)
[6, 7, 2, 1]
>>> random.sample(lst, 4)
[6, 3, 1, 0]
>>> random.seed('just some random seed') # use the same seed again
>>> random.sample(lst, 4)
[6, 7, 2, 1]
>>> random.sample(lst, 4)
[6, 3, 1, 0]
import random
list = [] # your list of numbers that range from 0 -9
# this seed will always give you the same pattern of random numbers.
random.seed(12) # I randomly picked a seed here;
# repeat this as many times you need to pick from your list
index = random.randint(0,len(list))
random_value_from_list = list[index]
If you got numpy loaded, you can use np.random.permutation. If you give it a single integer as argument it returns a shuffled array with the elements from np.arange(x), if you give it a list like object the elements are shuffled, in case of numpy arrays, the arrays are copied.
>>> import numpy as np
>>> np.random.permutation(10)
array([6, 8, 1, 2, 7, 5, 3, 9, 0, 4])
>>> i = list(range(10))
>>> np.random.permutation(i)
array([0, 7, 3, 8, 6, 5, 2, 4, 1, 9])

Categories

Resources