Python: Replacing elements in the list with adjacent data points - python

I currently have a list like this:
A = [1, 2, "NaN", 4, 5,"NaN"]
If an element is null, I would like to replace it with the average value of adjacent values
so A[3] would be replaced by 3 (2+4/2).
If there is only one adjacent value as shown by A[5], then return the one adjacent value itself.
Desired output:
A = [1, 2, 3, 4, 5, 5]

I'm sure there is a better way, but here is one way to do it. It handles the case where NaN appears at the end of the list.
Code:
A = [1, 2, "NaN", 4, 5,"NaN"]
for i in range(len(A)):
if A[i] == "NaN":
if i == 0: A[i] = A[1]
elif i == len(A) - 1: A[i] = A[i - 1]
else: A[i] = (A[i - 1] + A[i + 1]) // 2
print(A)
Output:
[1, 2, 3, 4, 5, 5]

Related

Need to remove all duplicated values in list

I am trying to solve this question "Good array is the array that does not contain any equal adjacent elements.
Given an integer array A of length N, you are asked to do some operations (possibly zero) to make the
array good. One operation is to remove two equal adjacent elements and concatenate the rest of the array.
A : (1, 2, 2, 3, 4)
An after-one operation : (1, 3, 4)
" With python list, as follow:
L=[]
n=int(input("Enter a num"))
for _ in range(n):
x=int(input())
L.append(x)
print(L)
for z in range(L):
if L[z]==L[z+1]:
L.remove(z)
L.remove(z+1)
print(L)
I keep getting this error: Input In [37]
if L[z]==L[z+1]
^
SyntaxError: invalid syntax
any solutions!!`
Easiest solution is to groupby the array and just keep the groups with only one item:
>>> A = [1, 2, 2, 3, 4]
>>> import itertools
>>> A = [i for i, g in itertools.groupby(A) if len(list(g)) == 1]
>>> A
[1, 3, 4]
Edit
This is NOT correct solution of the above problem. It removes contiguous duplicates from the list. But in question, we need to keep 1 instance of duplicate element.
Please refer to #funnydman solution. Thanks #funnydman for pointing out the mistake :)
You are iterating the list and deleting items from the same :|
Not a good idea!
You can use another list to store the result.
result = []
for z in range(len(L)-1):
if L[z]!=L[z+1]:
result.append(L[z])
result.append(L[-1])
Unfortunately, Nawal's solution does not produce the correct answer, I would suggest this approach:
def get_good_array(alist):
result = []
L = alist + [None]
i = 0
while i < len(L) - 1:
if L[i] != L[i + 1]:
result.append(L[i])
i += 1
else:
i += 2
return result
assert get_good_array([1, 2, 2, 3, 4]) == [1, 3, 4]
assert get_good_array([1, 1, 2, 2, 3, 4]) == [3, 4]
assert get_good_array([0, 1, 1, 2, 2, 3, 4]) == [0, 3, 4]
assert get_good_array([1, 3, 4, 4]) == [1, 3]
assert get_good_array([1, 3, 4, 4, 5]) == [1, 3, 5]
You are missing a : at the end of the if statement, your code should look like the following
L=[]
n=int(input("Enter a num"))
for _ in range(n):
x=int(input())
L.append(x)
print(L)
for z in range(L):
if L[z]==L[z+1]:
L.remove(z)
L.remove(z+1)
print(L)
I guess using <list>.count() is quite intuitive approach. As following,
def deduplicate(l: list):
return [value for value in l if l.count(value) == 1]
assert deduplicate([2]) == [2]
assert deduplicate([2, 2]) == []
assert deduplicate([2, 2, 3]) == [3]
assert deduplicate([2, 2, 2]) == []
assert deduplicate([2, 2, 2, 3]) == [3]

How to shift items in an array by a "K" number of times?

Shift the items in the given array, by some number of times, as shown in the below examples;
array = [1, 2 ,3 , 4, 5, 6]
k1 = 2
k2 = -3
k3 = 20
test1:
cirShift(array, k1)
Result: [5, 6, 1, 2, 3, 4]
test2:
cirShift(array, k2)
Result: [4, 5, 6, 1, 2, 3]
test3:
cirShift(array, k3)
Result: [5, 6, 1, 2, 3, 4]
I have used the below to achieve the right-rotate a list by k positions;
def rightRotateByOne(A):
Fin= A[-1]
for i in reversed(range(len(A) - 1)):
A[i + 1] = A[i]
A[0] = Fin
def rightRotate(A, k):
if k < 0 or k >= len(A):
return
for i in range(k):
rightRotateByOne(A)
if __name__ == '__main__':
A = [1, 2, 3, 4, 5, 6, 7]
k = 3
rightRotate(A, k)
print(A)
As of now, able to obtain results for test1 but would like to achieve the test2 and test3
Even easier, split the array in half given the boundary, swap and glue back:
def cirShift(a, shift):
if not shift or not a:
return a
return a[-shift%len(a):] + a[:-shift%len(a)]
Courtesy of #KellyBundy, a short-circut one-liner:
def cirShift(a, shift):
return a and a[-shift%len(a):] + a[:-shift%len(a)]
I think this question may be an exercise in self learning ('how to do X using just python'), so my answer is auxiliary, but you can always use np.roll():
#test 1
import numpy as np
a = [1, 2 ,3, 4, 5, 6]
np.roll(a, 2)
gives output
[5, 6, 1, 2, 3, 4]
and
#test 2
np.roll(a, -2)
gives output
[3, 4, 5, 6, 1, 2]
Even if you give a number that is larger than the array size, it handles the overflow:
#test 3
np.roll(a, 10)
gives output
[3, 4, 5, 6, 1, 2]
Rolling also works in multiple dimension arrays and across specified axes, which is pretty neat.
def shift(l, shift_t):
r = [*l]
for n in range(abs(shift_t)):
if shift_t < 0:
r = r[1:] + [r[0]]
else:
r = [r[-1]] + r[:-1]
return r
The key is to take one item of the list and place it on the opposite side, which is essentially all that shifting is doing. If you shift negative, you put the first one at the end, and if you shift positive, you put the last one at the beginning.

Problems in Quick Sort Code

I am having a problem with my Quick Sort code. I am new to coding and python(python 3.6), any help would be really appreciated. I have seen several implementations of Quick Sort online but I want to figure out what is really wrong with my code. I am pasting my code below:
def Partition(A):
q = A[0]
i = 1
for j in range(1, len(A)):
if A[j] < q:
A[j], A[i] = A[i], A[j]
i = i + 1
A[0], A[i - 1] = A[i - 1], A[0]
return i - 1
def QuickSort(A):
if len(A) <= 1:
return A
else:
q = Partition(A)
QuickSort(A[ : q])
QuickSort(A[q + 1 : ])
return A
A = [7, 5, 4, 1, 3, 6, 2, 8]
Sorted = []
Sorted = QuickSort(A)
print(Sorted)
For the input above I am getting the output [2, 5, 4, 1, 3, 6, 7, 8] instead of getting a sorted list in ascending order.
These try to sort copied parts of A:
QuickSort(A[ : q])
QuickSort(A[q + 1 : ])
They return something, but you ignore what they return, so it's lost. You should write their results back into A:
A[ : q] = QuickSort(A[ : q])
A[q + 1 : ] = QuickSort(A[q + 1 : ])
After this change, your result is the expected [1, 2, 3, 4, 5, 6, 7, 8].

Python: Removing a specific item that has a duplicate only knowing its index in a list

What I want this program to do is to delete all the 1's that are in the list that follow by index all the 2's.
This is the code:
a = [3, 1, 2, 1]
a.remove(a[3])
print(a)
I really put this program into the basics but the point is that I have only got the index of the 1 that follows the 2, tho when I do this the result is:
[3, 2, 1]
This means that it doesn't delete the 1 I want but just the first 1 it finds. Tho this is really a problem in a real program situation where the first 1 is an important value.
How can I fix it? Thanks
You can use list comprehension:
a = [3, 1, 2, 1]
new_a = [c for i, c in enumerate(a) if c != 1 and i+1 < len(a) and a[i+1] != 2]+[a[-1]]
Output:
[3, 2, 1]
I would use a good-ol' explicit loop for this one:
a = [3, 1, 2, 1]
b = [a[0]]
for i, item in enumerate(a[1:], 1):
if item == 1 and a[i-1] == 2:
continue
else:
b.append(item)
print(b) # -> [3, 1, 2]
Note that a does not get modified but rather a new list b is created.
If you want to remove a specific index you use del.
a = [3, 1, 2, 1]
del a[3]
print(a)
Output:
[3, 1, 2]
The del keyword might be your best bet here, for example:
a = [3, 1, 2, 1]
index = 1
while index < len(a):
if a[index - 1] == 2 and a[index] == 1:
del a[index]
else:
index += 1
print(a)
I don't know about the more pythonic way to do this, but you can keep a counter to check for the second appearance of a value you want to delete from a list.
a = [3, 1, 2, 1]
counter = 0
for i, v in enumerate(a):
if v==1:
if counter == 1:
del a[i]
else:
counter += 1
print a
#output: [3, 1, 2]
Using enumerate() and a list comprehension:
>>> a = [3, 1, 2, 1]
>>> [v for i, v in enumerate(a) if not i or not (v == 1 and a[i-1] == 2)]
[3, 1, 2]
Using list comprehension with enumerate(a) and index(2)
[j for i,j in enumerate(a) if not (i>a.index(2) and j==1)]
#[3, 1, 2]

Alternatives to index() in Python

So for my assignment I must find a way in which I can work around this problem of printing 'YES' if a list contains the elements 1,2,3 in the consecutive order. It does not work if the list contains the elements [3,1,2,3] due to the index method. How can I work around this?
n=int(input("Enter the number of elements: "))
A=[]
for i in range(0,n):
print("Entering element", i)
LstEl=int(input("Please enter the element: "))
A.append(LstEl)
print(A)
for i in range(0,len(A)):
if(1 in A and 2 in A and 3 in A):
post1 = A.index(1)
post2 = A.index(2)
post3 = A.index(3)
if(post1 < post2 and post2 < post3):
print("YES")
break
else:
print('NO')
break
else:
print("NO")
break
Thanks!
one option is just:
# find the indices of all `1`s
one_idxs = (i for (i, v) in enumerate(values) if v == 1)
for idx in one_idxs:
if values[i : i + 3] == [1, 2, 3]:
print('YES')
break
else:
print('NO')
a more concise way is
if any(values[i : i + 3] == [1, 2, 3] for (i, v) in enumerate(values) if v == 1):
print('YES')
else:
print('NO')
Once you find a valid post1 value, you can check for the sequence using
if A[post1:post1+3] == [1, 2, 3]:
print('Yes')
break
Use the other parameters of the index() method to find successive occurrences of the element '1'.
The following code uses a generator function to extract sublists from a larger list. It's probably not appropriate for a homework assignment if you don't understand the mechanics behind generator functions, but it might be something to look into if you're interested.
# A generator function that returns the n-length sublists of list lst
def slider(lst, n):
start = 0
while start + n <= len(lst):
yield lst[start:start+n]
start += 1
# A function that will return True if sequence needle exists in
# haystack, False otherwise
def list_contains(haystack, needle):
for sub in slider(haystack, 3): # Loop through the sublists...
if sub == needle: # ... test for equality ...
return True
return False
# Code
big = [2,4,6,8,0,1,2,3,1,5,7] # Hardcoded here, could be created
# in a loop like you show
seq = [1,2,3] # The sequence you're looking for
print(list_contains(big, seq))
You can see the output of the generator function with something like:
big = [2,4,6,8,0,1,2,3,1,5,7]
for sub in slider(big, 3):
print(sub)
Output:
[2, 4, 6]
[4, 6, 8]
[6, 8, 0]
[8, 0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 1]
[3, 1, 5]
[1, 5, 7]
Or maybe more clearly:
# [2, 4, 6, 8, 0, 1, 2, 3, 1, 5, 7]
[2, 4, 6]
[4, 6, 8]
[6, 8, 0]
[8, 0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 1]
[3, 1, 5]
[1, 5, 7]
I would suggest using enumerate with the for loop.
lst = ['a', 'b', 'c', 'd']
for i, value in enumerate(lst):
print(i, value) # 0,a.....3,d

Categories

Resources