Python: Sort optimally and compare - python

How do I arrange a list A such that the maximum number of elements of list A are greater than another list B?
EXAMPLE:
A='3 6 7 5 3 5 6 2 9 1'
B='2 7 0 9 3 6 0 6 2 6'
answer=7..................If I sort the lists,
sListA = [1, 2, 3, 3, 5, 5, 6, 6, 7, 9]
sListB = [0, 0, 2, 2, 3, 6, 6, 6, 7, 9]
#if sListA[i]>sListB[j]: count +=1
There are 5 instances where sListA[i] > sListB[j]
but we need to maximize instances where sListA[i] > sListB[j].., ie, 7
If sListA was [1, 2, 3, 3, 5, 7, 9, 5, 6, 6]
and slistB was[0, 0, 2, 2, 3, 6, 6, 6, 7, 9]
then 7 instances of sListA[i] > sListB[j] will be possible..
Heres my code:
def main():
listA=list(map(int,A.rstrip().split()))
listB=list(map(int,B.rstrip().split()))
sListA=sorted(listA)
sListB=sorted(listB)
count=0
for (i,j) in map(sListA,sListB):
if sListA[i]>sListB[j]:
count+=1
print(count)
main()
But this only counts from sorted lists, I need to find a way to swap elements with the next largest element in sListA when sListA[i]<sListB[j]
to maximize instances where sListA[i]>sListB[j]

maybe not the most efficient but you can try
a = [1, 2, 3, 3, 5, 5, 6, 6, 7, 9]
b = [0, 0, 2, 2, 3, 6, 6, 6, 7, 9]
b.sort()
a.sort()
new_a = []
for bb in b:
idx_v = np.where(np.array(a) > bb)
if idx_v[0].size == 0:
break
else:
e = a.pop(np.min(idx_v[0]))
new_a.append(e)
new_a.extend(a)
you'll find that
np.sum([new_a[n] > b[n] for n in range(len(b))])
is 7, and
new_a
[1, 2, 3, 3, 5, 7, 9, 5, 6, 6]

So in theory this should do what you wanted:
A='3 6 7 5 3 5 6 2 9 1'
B='2 7 0 9 3 6 0 6 2 6'
def main():
listA = sorted(list(map(int,A.rstrip().split())))
listB = sorted(list(map(int,B.rstrip().split())))
j = 0
for i in range(len(listA)):
if listA[i] <= listB[j]:
i += 1
else:
listA[i], listA[j] = listA[j], listA[i]
j += 1
return listA
print(main())
Returns:
[1, 2, 3, 3, 5, 7, 9, 6, 5, 6]
In essence, all I did was add an else to your if structure to keep i counting and swap elements over in listA
Previous answer (if you are interested in the outcome of 7 specifically):
A='3 6 7 5 3 5 6 2 9 1'
B='2 7 0 9 3 6 0 6 2 6'
def main():
listA = sorted(list(map(int,A.rstrip().split())))
listB = sorted(list(map(int,B.rstrip().split())))
cnt = 0
j = 0
for i in range(len(listA)):
if listA[i] > listB[j]:
cnt += 1
j += 1
else:
i += 1
return cnt
print(main())
To do this for multiple lists of A and B maybe try to apply something like:
Alist=['3 6 7 5 3 5 6 2 9 1', '9 5 3 1 0']
Blist=['2 7 0 9 3 6 0 6 2 6', '5 4 3 4 6']
def main():
cnt = 0
for x in range(len(Alist)):
listA = sorted(list(map(int,Alist[x].rstrip().split())))
listB = sorted(list(map(int,Blist[x].rstrip().split())))
j = 0
for i in range(len(listA)):
if listA[i] > listB[j]:
cnt += 1
j += 1
else:
i += 1
return cnt
print(main())

Related

How do I get the diagonal elements of a matrix/grid from a specific element?

I have an 8x8 grid of different numbers, and I want to get the elements of the diagonal that contains a given starting position. Here is an example
l = [[str(randint(1,9)) for i in range(8)] for n in range(8)]
>> [
[1 5 2 8 6 9 6 8]
[2 2 2 2 8 2 2 1]
[9 5 9 6 8 2 7 2]
[2 8 8 6 4 1 8 1]
[2 5 5 5 4 4 7 9]
[3 9 8 8 9 4 1 1]
[8 9 2 4 2 8 4 3]
[4 4 7 8 7 5 3 6]
]
How would I go about getting the diagonal from the position x=4 and y=3 (so 4th list and 5th element in that list)? So The diagonal I would want would be [5,2,6,4,4,1,3].
You can calculate the row and column of the top-left item of the diagonal based on the difference of x and y, and the number of iterations based on the difference between the lower of the two boundaries and the higher of the starting row and column:
def diagonal(m, x, y):
row = max((y - x, 0))
col = max((x - y, 0))
for i in range(min((len(m), len(m[0]))) - max((row, col))):
yield m[row + i][col + i]
so that:
m = [
[1, 5, 2, 8, 6, 9, 6, 8],
[2, 2, 2, 2, 8, 2, 2, 1],
[9, 5, 9, 6, 8, 2, 7, 2],
[2, 8, 8, 6, 4, 1, 8, 1],
[2, 5, 5, 5, 4, 4, 7, 9],
[3, 9, 8, 8, 9, 4, 1, 1],
[8, 9, 2, 4, 2, 8, 4, 3],
[4, 4, 7, 8, 7, 5, 3, 6],
]
print(list(diagonal(m, 4, 3)))
outputs:
[5, 2, 6, 4, 4, 1, 3]
Here is what I came up with. It's not beautiful but it gets the job done.
def get_diagonal(full_grid, y, x):
if x > y:
while y >= 1:
x -= 1
y -= 1
else:
while x >= 1:
x -= 1
y -= 1
diagonal = []
while x < len(grid) and y < len(grid[0]):
diagonal.append(grid[x][y])
x += 1
y += 1
return diagonal
grid = [
[1, 5, 2, 8, 6, 9, 6, 8],
[2, 2, 2, 2, 8, 2, 2, 1],
[9, 5, 9, 6, 8, 2, 7, 2],
[2, 8, 8, 6, 4, 1, 8, 1],
[2, 5, 5, 5, 4, 4, 7, 9],
[3, 9, 8, 8, 9, 4, 1, 1],
[8, 9, 2, 4, 2, 8, 4, 3],
[4, 4, 7, 8, 7, 5, 3, 6]]
get_diagonal(grid, 5, 3)
It seems counter-intuitive to me to use "y" along the row and "x" along the vertical, so I've swapped them. If you're OK with starting the indexing at zero, then this worked for me:
from random import randint
l = [[str(randint(1,9)) for i in range(8)] for n in range(8)]
# Show the grid
for row in l:
print(' '.join([str(n) for n in row]))
# Give the initial position, then follow the diagonal down and
# to the right until you run out of rows or columns.
x_pos = 1
y_pos = 3
diag = []
while x_pos < len(l[0]) and y_pos < len(l):
diag.append(l[y_pos][x_pos])
x_pos += 1
y_pos += 1
print(diag)
Sample output:
1 3 8 7 3 1 5 2
4 5 8 6 9 4 3 2
2 6 1 3 8 6 8 1
7 1 8 2 7 4 7 4
9 5 5 5 5 2 3 1
8 5 9 7 2 7 1 8
3 3 3 4 2 9 8 3
3 2 8 6 2 4 4 8
['1', '5', '7', '2', '4']
From #blhsing answer, I think the number iterations should be min(len(m) - 1 - row, len(m[0]) - 1 - col) instead.
Let's see an example:
Imagine a matrix with 2 rows:
[
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
]
Let's say the starting point is the first row, element number four, if we use the original calculation (which is min((len(m), len(m[0]))) - max((row, col))) we'll end-up with (min(2,5) - max(0, 3)) = 2 - 3 = -1.
If we use the proposed one (which is min(len(m) - 1 - row, len(m[0]) - 1 - col)), we'll have min(2 - 1 - 0, 5 - 1 - 3) = min(1, 1) = 1.

Group array elements according occurence, keep order, get first and last indices

I wonder if there is a nicer way in pandas to achieve the same:
x = [1, 1, 1, 2, 2, 2, 3, 3, 3, 5, 5, 1, 1, 2, 2]
x = np.asarray(x)
df = pd.DataFrame(columns=['id', 'start', 'end'])
if len(x) > 1:
i = 0
for j in range(1, len(x)):
if x[j] == x[j-1]:
continue
else:
df.loc[len(df)] = [x[i], i, j-1]
i = j;
df.loc[len(df)] = [x[i], i, j]
else:
df.loc[len(df)] = [x[0], 0, 0]
The output looks like this
[1 1 1 2 2 2 3 3 3 5 5 1 1 2 2]
id start end
0 1 0 2
1 2 3 5
2 3 6 8
3 5 9 10
4 1 11 12
5 2 13 14
Thanks for helpful hints.
Here's a way you could do it using numpy:
x = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3, 5, 5, 1, 1, 2, 2])
# Search for all consecutive non equal values in the array
vals = x[x != np.roll(x, 1)]
# array([1, 2, 3, 5, 1, 2])
# Indices where changes in x occur
d = np.flatnonzero(np.diff(x) != 0)
# array([ 2, 5, 8, 10, 12])
start = np.hstack([0] + [d+1])
# array([ 0, 3, 6, 9, 11, 13])
end = np.hstack([d, len(x)-1])
# array([ 2, 5, 8, 10, 12, 14])
pd.DataFrame({'id':vals, 'start':start, 'end':end})
id start end
0 1 0 2
1 2 3 5
2 3 6 8
3 5 9 10
4 1 11 12
5 2 13 14
Another solution:
df= pd.DataFrame(data=[1, 1, 1, 2, 2, 2, 3, 3, 3, 5, 5, 1, 1, 2, 2],columns=['id'])
g=df.groupby((df.id!=df.id.shift()).cumsum())['id']
df_new=pd.concat([g.first(),g.apply(lambda x: x.duplicated(keep='last').idxmax()),\
g.apply(lambda x: x.duplicated(keep='last').idxmin())],axis=1)
df_new.columns=['id','start','end']
print(df_new)
id start end
id
1 1 0 2
2 2 3 5
3 3 6 8
4 5 9 10
5 1 11 12
6 2 13 14
You could do the following, using only pandas:
import numpy as np
import pandas as pd
x = [1, 1, 1, 2, 2, 2, 3, 3, 3, 5, 5, 1, 1, 2, 2]
s = pd.Series(x)
# store group-by to avoid repetition
groups = s.groupby((s != s.shift()).cumsum())
# get id and size for each group
ids, size = groups.first(), groups.size()
# get start
start = size.cumsum().shift().fillna(0).astype(np.int32)
# get end
end = (start + size - 1)
df = pd.DataFrame({'id': ids, 'start': start, 'end': end}, columns=['id', 'start', 'end'])
print(df)
Output
id start end
1 1 0 2
2 2 3 5
3 3 6 8
4 5 9 10
5 1 11 12
6 2 13 14
using itertools.groupby
import pandas as pd
from itertools import groupby
x = [1, 1, 1, 2, 2, 2, 3, 3, 3, 5, 5, 1, 1, 2, 2]
l = []
for i in [list(g) for _,g in groupby(enumerate(x), lambda x:x[1])]:
l.append( (i[0][1], i[0][0], i[-1][0]) )
print (pd.DataFrame(l, columns=['id','start','end']))
Output:
id start end
0 1 0 2
1 2 3 5
2 3 6 8
3 5 9 10
4 1 11 12
5 2 13 14

Get top K values in pandas series include recurring values

I have a code snippet in python. It gets top K=5 values but don't increment the value of K if the value has already occurred.
For example upon giving [1, 3, 3, 5, 5, 6, 1, 4, 8, 9, 34, 66, 124] and K = 5, it should return
[1, 3, 3, 5, 5, 6, 1, 4]
Here if a value is repeating then it should not increment the value of K. Here is the Python code. But how can I do it in pandas Series?.
def get_top_K_uniques(K, nums):
ret = []
presense = defaultdict(bool)
counter = 0
for each in nums:
if not presense[each]:
presense[each] = True
counter+=1
ret.append(each)
if counter == K:
return ret
Thanks in advance.
Using Series.unique() and Series.isin()
nums = pd.Series([1, 3, 3, 5, 5, 6, 1, 4, 8, 9, 34, 66, 124])
uniq = nums.unique()[:5]
nums[nums.isin(uniq)]
Output
0 1
1 3
2 3
3 5
4 5
5 6
6 1
7 4
Using category
s[s.astype('category').cat.codes<4]
Out[153]:
0 1
1 3
2 3
3 5
4 5
6 1
7 4
dtype: int64

recursion, Python, countup, countdown

I am supposed to write a recursive function counting(5) that prints 5 4 3 2 1 0 1 2 3 4 5.
I have made two functions down below that do half part each but I need them to be put together.
def countdown(n):
if n == 0:
print 0
else:
print n,
countdown(n-1)
def countup(n):
if n >= 1:
countup(n - 1)
print n,
I suppose the trick is to understand the recursion point does not end execution:
def count_down_up(n):
if not n:
print n # prints 0 and terminates recursion
return
print n # print down 5, 4, 3, 2, 1
count_down_up(n-1) # recursion point
print n # prints up 1, 2, 3, 4, 5
You can see each step print n, <RECURSION>, n, which unfolds to:
5, <count_up_down 4>, 5
5, 4, <count_up_down 3>, 4, 5
# ...
5 ,4, 3, 2, 1, <count_up_down 0>, 1, 2, 3, 4, 5 # recursion stops ...
5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5
#Reut_Sharabani solution is fine but I think this is simpler to read :
def countdown(N,n):
if abs(n) > N:
return
else:
print(abs(n))
countdown(N,n-1)
Call like this :
countdown(5,5)
One way to do it is by keeping track of 2 lists during the recursion and then stiching them together at return at the end.
def countdown_up(n, i=0, down=[], up=[] ):
if n >i:
return countdown_up(n-1, i, down+[n], [n]+up)
else:
# stich the 2 lists together with the low point [i]
return down+[i]+up
# by default it counts down to 0.
>>>countdown_up(5)
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5]
# you can run with any arbitrary high and low numbers.
>>>countdown_up(10, 3)
[10, 9, 8, 7, 6, 5, 4, 3, 4, 5, 6, 7, 8, 9, 10]
To have the function print instead of return list we just need to change 1 line.
def countdown_up(n, i=0, down=[], up=[] ):
if n >i:
return countdown_up(n-1, i, down+[n], [n]+up)
else:
print(" ".join("%s "%x for x in down+[i]+up))
>>>countdown_up(5)
5 4 3 2 1 0 1 2 3 4 5
>>>countdown_up(10,3)
10 9 8 7 6 5 4 3 4 5 6 7 8 9 10

how do i do round-robin in python 3

i have to do a rotation of a list. I get a sorted list and i have to get minimum of pairs and maximum of tree numbers in a permutation of this sorted list as an answear. It has to have different numbers sorted from lowest to highest. For example:
MyList=[1, 1, 1, 2, 2, 2, 3, 3, 3, 3]
and the output must be:
1 2 3
1 2 3
1 3
2 3
and for :
MyList=[1, 1, 1, 1, 1, 2, 3, 4, 5, 6]
and the output must be:
1 2
1 3
1 4
1 5
1 6
I saw i can do it with a method called round-robin but i don't know how.
Thank you for your help!
from itertools import cycle
A = [[1,2,3],[4,5,6],[7]]
B = [[8],[9,10,11],[12,13]]
for p in A:
max1 = len(p) if max1 <len(p) else max1
for p in B:
max1 = len(p) if max1 <len(p) else max1
i = len(A)
j = 0
C = []
list_num = cycle(k for k in range(i))
for x in list_num:
j += 1
if j == i*3:
break
if A[x]:
C.append(A[x].pop(0))
if B[x]:
C.append(B[x].pop(0))
Output:
[1, 8, 4, 9, 7, 12, 2, 5, 10, 13, 3, 6, 11]

Categories

Resources