Create a multiset from a Set X - python

in a Multiset it is allowed to have multiple elements
For Example. if X (normal set) = {0,2,4,7,10}, then ∆X (multiset) = {2,2,3,3,4,5,6,7,8,10}.
∆X denotes the multiset of all 􏰃(N 2) pairwise distances between points in X
How can i Write this in Python?
I have created a List X but i don't know how to put all differences in another list and order them.
I hope you can help me.

It is basically just one line.
import itertools
s = {0,2,4,7,10}
sorted([abs(a-b) for (a,b) in itertools.combinations(s,2)])

you can use itertools
import itertools
s = {0,2,4,7,10}
k = itertools.combinations(s,2)
distance = []
l = list(k)
for p in l:
distance.append(abs(p[1]-p[0]))
print(sorted(distance))

A simple way is to convert your set to a list, sort it, and then use a double for loop to compute the differences:
X = {0,2,4,7,10} # original set
sorted_X = sorted(list(X))
diffs = []
for i, a in enumerate(sorted_X):
for j, b in enumerate(sorted_X):
if j > i:
diffs.append(b-a)
print(diffs)
#[2, 4, 7, 10, 2, 5, 8, 3, 6, 3]
And if you want the diffs sorted as well:
print(sorted(diffs))
#[2, 2, 3, 3, 4, 5, 6, 7, 8, 10]
Another option that would work in this case is to use itertools.product:
from itertools import product
print(sorted([(y-x) for x,y in product(sorted_X, sorted_X) if y>x]))
#[2, 2, 3, 3, 4, 5, 6, 7, 8, 10]

Related

how to make sure that two numbers next to each other in a list are different

I have a simple code that generates a list of random numbers.
x = [random.randrange(0,11) for i in range(10)]
The problem I'm having is that, since it's random, it sometimes produces duplicate numbers right next to each other. How do I change the code so that it never happens? I'm looking for something like this:
[1, 7, 2, 8, 7, 2, 8, 2, 6, 5]
So that every time I run the code, all the numbers that are next to each other are different.
x = []
while len(x) < 10:
r = random.randrange(0,11)
if not x or x[-1] != r:
x.append(r)
x[-1] contains the last inserted element, which we check not to be the same as the new random number. With not x we check that the array is not empty, as it would generate a IndexError during the first iteration of the loop
Here's an approach that doesn't rely on retrying:
>>> import random
>>> x = [random.choice(range(12))]
>>> for _ in range(9):
... x.append(random.choice([*range(x[-1]), *range(x[-1]+1, 12)]))
...
>>> x
[6, 2, 5, 8, 1, 8, 0, 4, 6, 0]
The idea is to choose each new number by picking from a list that excludes the previously picked number.
Note that having to re-generate a new list to pick from each time keeps this from actually being an efficiency improvement. If you were generating a very long list from a relatively short range, though, it might be worthwhile to generate different pools of numbers up front so that you could then select from the appropriate one in constant time:
>>> pool = [[*range(i), *range(i+1, 3)] for i in range(3)]
>>> x = [random.choice(random.choice(pool))]
>>> for _ in range(10000):
... x.append(random.choice(pool[x[-1]]))
...
>>> x
[0, 2, 0, 2, 0, 2, 1, 0, 1, 2, 0, 1, 2, 1, 0, ...]
O(n) solution by adding to the last element randomly from [1,stop) modulo stop
import random
x = [random.randrange(0,11)]
x.extend((x[-1]+random.randrange(1,11)) % 11 for i in range(9))
x
Output
[0, 10, 4, 5, 10, 1, 4, 8, 0, 9]
from random import randrange
from itertools import islice, groupby
# Make an infinite amount of randrange's results available
pool = iter(lambda: randrange(0, 11), None)
# Use groupby to squash consecutive values into one and islice to at most 10 in total
result = [v for v, _ in islice(groupby(pool), 10)]
Function solution that doesn't iterate to check for repeats, just checks each add against the last number in the list:
import random
def get_random_list_without_neighbors(lower_limit, upper_limit, length):
res = []
# add the first number
res.append(random.randrange(lower_limit, upper_limit))
while len(res) < length:
x = random.randrange(lower_limit, upper_limit)
# check that the new number x doesn't match the last number in the list
if x != res[-1]:
res.append(x)
return res
>>> print(get_random_list_without_neighbors(0, 11, 10)
[10, 1, 2, 3, 1, 8, 6, 5, 6, 2]
def random_sequence_without_same_neighbours(n, min, max):
x = [random.randrange(min, max + 1)]
uniq_value_count = max - min + 1
next_choises_count = uniq_value_count - 1
for i in range(n - 1):
circular_shift = random.randrange(0, next_choises_count)
x.append(min + (x[-1] + circular_shift + 1) % uniq_value_count)
return x
random_sequence_without_same_neighbours(n=10, min=0, max=10)
It's not to much pythonic but you can do something like this
import random
def random_numbers_generator(n):
"Generate a list of random numbers but without two duplicate numbers in a row "
result = []
for _ in range(n):
number = random.randint(1, n)
if result and number == result[-1]:
continue
result.append(number)
return result
print(random_numbers_generator(10))
Result:
3, 6, 2, 4, 2, 6, 2, 1, 4, 7]

Can anyone help me in handling ties in a python list while I try to replace it's elements with their ranks?

I have a list that looks something like this:
lst_A = [32,12,32,55,12,90,32,75]
I want to replace the numbers with their rank. I am using this function to do this:
def obtain_rank(lstC):
sort_data = [(x,i) for i,x in enumerate(lstC)]
sort_data = sorted(sort_data,reverse=True)
result = [0]*len(lstC)
for i,(_,idx) in enumerate(sort_data,1):
result[idx] = i
return result
I am getting the following output while I use this:
[6, 8, 5, 3, 7, 1, 4, 2]
But what I want from this is:
[4, 7, 5, 3, 8, 1, 6, 2]
How can I go about this?
Try this:
import pandas as pd
def obtain_rank(a):
s = pd.Series(a)
return [int(x) for x in s.rank(method='first', ascending=False)]
#[4, 7, 5, 3, 8, 1, 6, 2]
You could use 2 loops:
l = [32,12,32,55,12,90,32,75]
d = list(enumerate(sorted(l, reverse = True), start = 1))
res = []
for i in range(len(l)):
for j in range(len(d)):
if d[j][1] == l[i]:
res.append(d[j][0])
del d[j]
break
print(res)
#[4, 7, 5, 3, 8, 1, 6, 2]
Here you go. In case, you are not already aware, please read https://docs.python.org/3.7/library/collections.html to understand defaultdict and deque
from collections import defaultdict, deque
def obtain_rank(listC):
sorted_list = sorted(listC, reverse=True)
d = defaultdict(deque) # deque are efficient at appending/popping elements at both sides of the sequence.
for i, ele in enumerate(sorted_list):
d[ele].append(i+1)
result = []
for ele in listC:
result.append(d[ele].popleft()) # repeating numbers with lower rank will be the start of the list, therefore popleft
return result
Update: Without using defaultdict and deque
def obtain_rank(listC):
sorted_list = sorted(listC, reverse=True)
d = {}
for i, ele in enumerate(sorted_list):
d[ele] = d.get(ele, []) + [i + 1] # As suggested by Joshua Nixon
result = []
for ele in listC:
result.append(d[ele][0])
del d[ele][0]
return result

Create a list of random integers and then put all numbers that are the same right beside eachother

def run():
lst=[]
for i in range(0,20):
ran = random.randint(1,10)
lst.append(ran)
return lst
So far I have created a list of random integers from 1 to 9 with 20 values, however how can I incorporate a swapping method so that values that are the same but not next to eachother will be next to eachother?
Thanks
You can build your own sorting criteria using indexes for the key argument.
import random
def run():
lst=[]
for i in range(0,20):
ran = random.randint(1,10)
lst.append(ran)
return lst
lst = run()
print(lst)
#[5, 10, 5, 1, 8, 10, 10, 6, 4, 9, 3, 9, 6, 9, 2, 9, 9, 1, 7, 8]
result = sorted(lst, key = lambda x: lst.index(x))
print(result)
#[5, 5, 10, 10, 10, 1, 1, 8, 8, 6, 6, 4, 9, 9, 9, 9, 9, 3, 2, 7]
Perhaps just sort the list:
lst = sorted(lst)
import random
#this is the function you gave with little edits, to see the changes it make
# after the process
def run():
lst=[]
for i in range(0,20):
ran = random.randint(1,10)
lst.append(ran)
print(lst)
swap(lst)
print(lst)
return lst
#this uses indexes of every element, and checks every other element of the list.
#this swap function works for lists with element made up of strings as well.
def swap(lst):
for i in range(len(lst)):
nu_m=lst[i]
x=i+1
while x<len(lst):
dump=i+1
acc=lst[i+1]
if(lst[i]==lst[x]):
lst[dump]=lst[x]
lst[x]=acc
x=x+1
x=run()
First let's create another list to keep the order of the unique numbers (like a set, but not sorted).
unsorted_set = []
for nb in lst:
if nb not in unsorted_set:
unsorted_set.append(nb)
Now that we got this list, let's create a final list that will continue that list but each number will be repeated n times, n is the occurences of the number in the first list. We will do this with lst.count()
final_list = []
for nb in unsorted_set:
for _ in range(lst.count(nb)):
final_list.append(nb)
Note that this code can be simplified a lot with List Comprehension.

dropping the lowest values from a list

I'm trying to write a python program that drops 25% of the lowest values from a list and (return the original unsorted list). For example;
Input : [1,5,6,72,3,4,9,11,3,8]
Output : [5,6,72,4,9,11,8]
I tried to do:
l = [1,5,6,72,3,4,9,11,3,8]
def drop(k):
while len(l)!=0 and k > 0:
k = k - 1
l.sort(reverse = True)
l.pop()
return l
k = math.ceil(len(l) * 0.25)
drop (k)
it returns [72, 11, 9, 8, 6, 5, 4] but is there a way to do it without sorting?.
You don't require to reverse sort and find the smallest element. Use min on list l which returns the smallest value from l and remove using l.remove conveniently.
import math
l = [1,5,6,72,3,4,9,11,3,8]
def drop(k):
while len(l)!=0 and k > 0:
k = k - 1
l.remove(min(l))
return l
k = math.ceil(len(l) * 0.25)
print(drop (k))
# [5, 6, 72, 4, 9, 11, 8]
You could use a heapq and keep popping elements until 25% of the container has been removed. Then, filter the contents of the original list
import heapq, copy
s = [1,5,6,72,3,4,9,11,3,8]
new_s = copy.deepcopy(s)
heapq.heapify(s)
count = 0
last_items = set()
while count/float(len(new_s)) <= 0.25:
last_items.add(heapq.heappop(s))
count += 1
final_s = [i for i in new_s if i not in last_items]
Output:
[5, 6, 72, 4, 9, 11, 8]
There are O(n) solutions to this problem. One of those, introselect, is implemented in numpy in the partition and argpartition functions:
>>> data = [1,5,6,72,3,4,9,11,3,8]
>>>
>>> k = int(round(len(data) / 4))
>>>
>>> import numpy as np
>>> dnp = np.array(data)
>>> drop_them = np.argpartition(dnp, k)[:k]
>>> keep_them = np.ones(dnp.shape, dtype=bool)
>>> keep_them[drop_them] = False
>>> result = dnp[keep_them].tolist()
>>>
>>> result
[5, 6, 72, 4, 9, 11, 3, 8]
Note that this method keeps one of the 3s and drops the other one in order to get the split at exactly k elements.
If instead you want to treat all 3s the same, you could do
>>> boundary = np.argpartition(dnp, k)[k]
>>> result = dnp[dnp > dnp[boundary]]
>>>
>>> result
array([ 5, 6, 72, 4, 9, 11, 8])
One way of doing this is this is very slow especially for longer lists!:
quart_len = int(0.25*len(l))
for i in range(quart_len):
l.remove(min(l))
A much faster way of doing this:
import numpy as np
from math import ceil
l = [1,5,6,72,3,4,9,11,3,8]
sorted_values = np.array(l).argsort()
l_new = [l[i] for i in range(len(l)) if i in sorted_values[int(ceil(len(l)/4.)):]]
Another approach:
l = np.array(l)
l = list(l[l > sorted(l)[len(l)/4]])
l1=[1,5,6,72,3,4,9,11,3,8]
l2=sorted(l1)
ln=round(len(l1)*0.25)
[i for i in l1 if i not in l2[ln+1:]]
Output:
[5, 6, 72, 4, 9, 11, 8]

In Python how can I change the values in a list to meet certain criteria

In Python, I have several lists that look like variations of:
[X,1,2,3,4,5,6,7,8,9,X,11,12,13,14,15,16,17,18,19,20]
[X,1,2,3,4,5,6,7,8,9,10,X,12,13,14,15,16,17,18,19,20]
[0,X,2,3,4,5,6,7,8,9,10,11,X,13,14,15,16,17,18,19,20]
The X can fall anywhere. There are criteria where I put an X, but it's not important for this example. The numbers are always contiguous around/through the X.
I need to renumber these lists to meet a certain criteria - once there is an X, the numbers need to reset to zero. Each X == a reset. Each X needs to become a zero, and counting resumes from there to the next X. Results I'd want:
[0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,10]
[0,1,2,3,4,5,6,7,8,9,10,0,1,2,3,4,5,6,7,8,9]
Seems like a list comprehension of some type or a generator could help me here, but I can't get it right.
I'm new and learning - your patience and kindness are appreciated. :-)
EDIT: I'm getting pummeled with downvotes, like I've reposted on reddit or something. I want to be a good citizen - what is getting me down arrows? I didn't show code? Unclear question? Help me be better. Thanks!
Assuming the existing values don't matter this would work
def fixList(inputList, splitChar='X'):
outputList = inputList[:]
x = None
for i in xrange(len(outputList)):
if outputList[i] == splitChar:
outputList[i] = x = 0
elif x is None:
continue
else:
outputList[i] = x
x += 1
return outputList
eg
>>> a = ['X',1,2,3,4,5,6,7,8,9,'X',11,12,13,14,15,16,17,18,19,20]
>>> fixList(a)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> b = ['y',1,2,3,4,5,6,7,8,9,10,'y',12,13,14,15,16,17,18,19,20]
>>> fixList(b, splitChar='y')
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
EDIT: fixed to account for the instances where list does not start with either X or 0,1,2,...
Using the string 'X' as X and the_list as list:
[0 if i == 'X' else i for i in the_list]
This will return the filtered list.

Categories