How to use exponents under special criteria - python-3.x

I am new to python and was trying to find a way to organize a specific function so I can take a list, apply special criteria to it, and then return another list.
I want to:
1) square a number if it is even
2) cube a number if it is odd
3) and then store those results in a list and return that list
Here is my code:
def square_function(x):
if i % 2 == 0:
x = [i ** (2)]
else:
y = [i ** (3)]
func = [x, y]
return func
I am very new to programming with python so any help you can give would be fantastic.

take a list - apply special criteria to it - and then return another list.
You're looking for the map() function
def foo(x):
return x**2 if x%2==0 else x**3
l = [1,2,3]
I = list(map(foo, l))

Using list comprehension:
>>> a = [1,2,3,4,5]
>>> [x ** 2 if x % 2 == 0 else x ** 3 for x in a]
[1, 4, 27, 16, 125]

I think that this could be what you are looking for:
from math import sqrt
def square_or_cube_function(x):
result = []
for i in x:
if i % 2 == 0:
result.append(sqrt(i))
else:
result.append(i ** 3)
return result
print(square_or_cube_function([1, 4, 5, 8]))
print(square_or_cube_function([5, 7, 16, 32]))
OUTPUT:
[1, 2.0, 125, 2.8284271247461903]
[125, 343, 4.0, 5.656854249492381]
A shorter solution could be:
from math import sqrt
def square_or_cube_function(x):
return [sqrt(i) if i % 2 == 0 else i **3 for i in x]
print(square_or_cube_function([1, 4, 5, 8]))
print(square_or_cube_function([5, 7, 16, 32]))
Same output.

Another LC solution, but using a bit of cleverness:
[x ** (x % 2 + 2) for x in L]

Related

Interpolation function for single data point

I'm using the interp1d function for interpolation
from scipy.interpolate import interp1d
x = [0, 3, 6, 10, 15, 20]
y = [1.87, 1.76, 1.27, 1.185, 0.995, 0.855]
f = interp1d(x, y, bounds_error=False)
x_find = [0, 5, 8, 10, 28]
print(f(x_find))
Using bounds_error=False in f = interp1d(x, y, bounds_error=False) returns nan value for x=28 in x_find.
Since interp1d raises an error for single datapoints, I tried the following for single datapoint.
x0 = [1.87]
y0 = [0.93]
f0 = lambda x: y0[0] if np.isclose(x, x0[0]) else np.NaN
print(f0(x0[0]))
This doesn't work when I try f0(x_find).
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Expected output:
f0(x_find) returns nan for values of x in x_find not present in x0like how bounds_error works.
Suggestions on how to do this will be really helpful
EDIT:
Question:
Would it be possible to modify the interpolation function of the single datapoint so that we could do just f(x_find), something similar to the one returned by f = interp1d() ?
I just only guess that you are missing a very simple thing: to put a single value to the f0() function using a list comprehension to get all values in case of a list with values.
Does the following code:
import numpy as np
from scipy.interpolate import interp1d
x = [0, 3, 6, 10, 15, 20]
y = [1.87, 1.76, 1.27, 1.185, 0.995, 0.855]
f = interp1d(x, y, bounds_error=False)
x_find = [0, 5, 8, 10, 28]
print(f(x_find))
x0 = [1.87]
y0 = [0.93]
f0 = lambda x: y0[0] if np.isclose(x, x0[0]) else np.NaN
print(f0(x0[0]))
print([f0(x) for x in x_find])
which prints:
[1.87 1.43333333 1.2275 1.185 nan]
0.93
[nan, nan, nan, nan, nan]
meet your expectations?
You can also redefine f0 to cover the case of passing a list of values to it as follows:
def f0(x):
import numpy as np
x0 = [1.87]
y0 = [0.93]
f = lambda x: y0[0] if np.isclose(x, x0[0]) else np.NaN
if isinstance(x, list):
return [f(z) for z in x]
elif isinstance(x, float):
return f(x)
else:
return "f0() accepts only float and lists of floats as parameter"
print('---')
print(f0(1.87))
print(f0(x_find))
print(f0("5"))
The output of the code above is:
---
0.93
[nan, nan, nan, nan, nan]
f0() accepts only float and lists of floats as parameter
FINALLY you can also redefine f0 as f_i which is a bit complex code simulating the behavior of scipy interp1d as follows:
def f_i(X=[0, 1.87], Y=[1.87, 0.93], bounds_error=False):
# ToDo: implementation of bounds_error evaluation
def f_interpolate(u):
assert len(X) > 1
XY = list(zip(X, Y))
XY.sort()
if not (XY[0][0] <= u <= XY[-1][0]):
return None
x_new = u
for i in range(len(XY)-1):
if XY[i][0] <= u <= XY[i+1][0]:
x_lo = XY[i ][0]
x_hi = XY[i+1][0]
y_lo = XY[i ][1]
y_hi = XY[i+1][1]
if x_new == x_lo:
return y_lo
if x_new == x_hi:
return y_hi
slope = (y_hi - y_lo) / (x_hi - x_lo)
y_new = y_lo + slope*(x_new - x_lo)
return y_new
return None
def f(v):
assert len(X) == 1
if v == X[0]:
return Y[0]
else:
return None
def r_f(w):
f_l = f_interpolate if len(X) > 1 else f
if isinstance(w, list):
return [f_l(z) for z in w]
elif isinstance(w, float):
return f_l(w)
else:
return "ValueErrorMessage: param. not float or list of floats"
return r_f
y = [1.87, 1.76, 1.27, 1.185, 0.995, 0.855]
x = [ 0, 3, 6, 10, 15, 20 ]
y0 = [0.93]
x0 = [1.87]
print('---')
f = f_i(x0, y0)
print(f(1.87))
f = f_i(x, y)
x_find = [0, 5, 8, 10, 28]
print(f(x_find))
print(f("5"))
which gives following output:
---
0.93
[1.87, 1.4333333333333333, 1.2275, 1.185, None]
ValueErrorMessage: param. not float or list of floats

Shuffling a deck of cards with Python [duplicate]

In Python, I've seen two variable values swapped using this syntax:
left, right = right, left
Is this considered the standard way to swap two variable values or is there some other means by which two variables are by convention most usually swapped?
Python evaluates expressions from left to right. Notice that while
evaluating an assignment, the right-hand side is evaluated before the
left-hand side.
Python docs: Evaluation order
That means the following for the expression a,b = b,a :
The right-hand side b,a is evaluated, that is to say, a tuple of two elements is created in the memory. The two elements are the objects designated by the identifiers b and a, that were existing before the instruction is encountered during the execution of the program.
Just after the creation of this tuple, no assignment of this tuple object has still been made, but it doesn't matter, Python internally knows where it is.
Then, the left-hand side is evaluated, that is to say, the tuple is assigned to the left-hand side.
As the left-hand side is composed of two identifiers, the tuple is unpacked in order that the first identifier a be assigned to the first element of the tuple (which is the object that was formerly b before the swap because it had name b)
and the second identifier b is assigned to the second element of the tuple (which is the object that was formerly a before the swap because its identifiers was a)
This mechanism has effectively swapped the objects assigned to the identifiers a and b
So, to answer your question: YES, it's the standard way to swap two identifiers on two objects.
By the way, the objects are not variables, they are objects.
That is the standard way to swap two variables, yes.
I know three ways to swap variables, but a, b = b, a is the simplest. There is
XOR (for integers)
x = x ^ y
y = y ^ x
x = x ^ y
Or concisely,
x ^= y
y ^= x
x ^= y
Temporary variable
w = x
x = y
y = w
del w
Tuple swap
x, y = y, x
I would not say it is a standard way to swap because it will cause some unexpected errors.
nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]
nums[i] will be modified first and then affect the second variable nums[nums[i] - 1].
Does not work for multidimensional arrays, because references are used here.
import numpy as np
# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)
# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)
See also Swap slices of Numpy arrays
To get around the problems explained by eyquem, you could use the copy module to return a tuple containing (reversed) copies of the values, via a function:
from copy import copy
def swapper(x, y):
return (copy(y), copy(x))
Same function as a lambda:
swapper = lambda x, y: (copy(y), copy(x))
Then, assign those to the desired names, like this:
x, y = swapper(y, x)
NOTE: if you wanted to you could import/use deepcopy instead of copy.
That syntax is a standard way to swap variables. However, we need to be careful of the order when dealing with elements that are modified and then used in subsequent storage elements of the swap.
Using arrays with a direct index is fine. For example:
def swap_indexes(A, i1, i2):
A[i1], A[i2] = A[i2], A[i1]
print('A[i1]=', A[i1], 'A[i2]=', A[i2])
return A
A = [0, 1, 2, 3, 4]
print('For A=', A)
print('swap indexes 1, 3:', swap_indexes(A, 1, 3))
Gives us:
('For A=', [0, 1, 2, 3, 4])
('A[i1]=', 3, 'A[i2]=', 1)
('swap indexes 1, 3:', [0, 3, 2, 1, 4])
However, if we change the left first element and use it in the left second element as an index, this causes a bad swap.
def good_swap(P, i2):
j = P[i2]
#Below is correct, because P[i2] is modified after it is used in P[P[i2]]
print('Before: P[i2]=', P[i2], 'P[P[i2]]=', P[j])
P[P[i2]], P[i2] = P[i2], P[P[i2]]
print('Good swap: After P[i2]=', P[i2], 'P[P[i2]]=', P[j])
return P
def bad_swap(P, i2):
j = P[i2]
#Below is wrong, because P[i2] is modified and then used in P[P[i2]]
print('Before: P[i2]=', P[i2], 'P[P[i2]]=', P[j])
P[i2], P[P[i2]] = P[P[i2]], P[i2]
print('Bad swap: After P[i2]=', P[i2], 'P[P[i2]]=', P[j])
return P
P = [1, 2, 3, 4, 5]
print('For P=', P)
print('good swap with index 2:', good_swap(P, 2))
print('------')
P = [1, 2, 3, 4, 5]
print('bad swap with index 2:', bad_swap(P, 2))
('For P=', [1, 2, 3, 4, 5])
('Before: P[i2]=', 3, 'P[P[i2]]=', 4)
('Good swap: After P[i2]=', 4, 'P[P[i2]]=', 3)
('good swap with index 2:', [1, 2, 4, 3, 5])
('Before: P[i2]=', 3, 'P[P[i2]]=', 4)
('Bad swap: After P[i2]=', 4, 'P[P[i2]]=', 4)
('bad swap with index 2:', [1, 2, 4, 4, 3])
The bad swap is incorrect because P[i2] is 3 and we expect P[P[i2]] to be P[3]. However, P[i2] is changed to 4 first, so the subsequent P[P[i2]] becomes P[4], which overwrites the 4th element rather than the 3rd element.
The above scenario is used in permutations. A simpler good swap and bad swap would be:
#good swap:
P[j], j = j, P[j]
#bad swap:
j, P[j] = P[j], j
You can combine tuple and XOR swaps: x, y = x ^ x ^ y, x ^ y ^ y
x, y = 10, 20
print('Before swapping: x = %s, y = %s '%(x,y))
x, y = x ^ x ^ y, x ^ y ^ y
print('After swapping: x = %s, y = %s '%(x,y))
or
x, y = 10, 20
print('Before swapping: x = %s, y = %s '%(x,y))
print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))
Using lambda:
x, y = 10, 20
print('Before swapping: x = %s, y = %s' % (x, y))
swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))
print('After swapping: x = %s, y = %s ' % swapper(x, y))
Output:
Before swapping: x = 10 , y = 20
After swapping: x = 20 , y = 10

Quick sort is recurring infinitely

First I would like to thank you in advance.
I tried to write a quick sort in python3, but it recurs infinitely.
Here's my code:
def partition(lst, l, h):
lst.append(float("inf"))
pivot = lst[0]
i, j = l+1, h
while i < j:
while lst[i] < pivot:
i += 1
while lst[j] > pivot:
j -= 1
if i < j:
lst[i] , lst[j] = lst[j], lst[i]
else:
lst = lst[1:i] + [pivot] + lst[i:]
return lst[:-1], i
def quickSort(lst, l, h):
if l < h-1:
mid = (l + h)//2
lst[l:h], mid = partition(lst[l:h], 0, h-l)
quickSort(lst, l, mid)
quickSort(lst, mid, h)
lst1 = [10, 12, 8, 16, 2, 6, 3, 9, 5]
quickSort(lst1, 0, 9)
In recursive algorithms, you should be careful about the initial step of the algorithm. For example, in your case, you need to sort the given array manually without any recursion with the size of 2.

Polynomials and dictionaries

i have this exercise about polynomials and dictionaries which i did (see below) but i am sure there is a better and easier way to solve it (for question 2 and 3). can anyone show me another way to approach question 2 or 3? Thanks.
here is the exercice:
In this exercise we want to work with polynomials of any degree. Each polynomial can be represented by a dictionary, whose keys correspond to the powers of x, and the values ​​to the coefficients. For example, to represent the polynomial x ^ 6 + 3 * x ^ 2, we can use the dictionary: {6: 1, 2: 3}
1. Write a function evaluer(p, x) that takes a polynomial p and a number x into arguments, and returns the value of polynomial at point x.
Execution example:
evaluer({3: 1, 1: 2, 0: -1}, 2)
OUT: 11
2. Write a function somme_polynomes(p1, p2) which takes two polynomials (dictionaries) into arguments and which
returns a new dictionary representing the sum of the two polynomials p1 and p2.
Execution example:
somme_polynomes ({3: 1, 2: 1, 0: 1}, {4: 2, 2: 3})
OUT: {0: 1, 2: 4, 3: 1, 4: 2}
3. Write a function produit_polynomes(p1, p2) that takes two polynomials as arguments and returns the product of two polynomials in a new dictionary.
Execution example:
produit_polynomes ({3: 1, 2: 1, 0: 1}, {4: 2, 2: 3})
OUT: {2: 3, 4: 5, 5: 3, 6: 2, 7: 2}
here is what i did:
# 1)
def evaluer(p,x):
c = 0
for key,value in p.items():
c += value*(x**key)
return c
# 2)
def somme_polynomes(p1,p2):
p3 = {}
for key,value in p1.items():
for k,v in p2.items():
p3.update({key:value})
p3.update({k:v})
for key in p1:
if key in p2:
add = p1[key]+p2[key]
p3.update({key:add})
if add == 0:
del p3[key]
return p3
# 3)
def produit_polynomes(p1,p2):
p3 = {}
for key,value in p1.items():
for k,v in p2.items():
if key+k in p3:
p3[key+k] += value*v
else:
p3.update({key+k:value*v})
return p3
Your code is fine, here are alternative ways of doing it using more of Python's language (generator expression, dict comprehension) and library (itertools, collections):
def evaluer(p, x):
return sum(v * x**k for k, v in p.items())
def somme_polynomes(p1, p2):
return {k: p1.get(k, 0) + p2.get(k, 0) for k in p1.keys() | p2.keys()}
import itertools as it
from collection import defaultdict
def produit_polynomes(p1, p2):
p3 = defaultdict(int)
for k1, k2 in it.product(p1, p2):
p3[k1+k2] += p1[k1]*p2[k2]
return dict(p3)
If you want to avoid importing any modules then produit_polnomes() could be written without the conditional as:
def produit_polynomes(p1,p2):
p3 = {}
for k1, v1 in p1.items():
for k2, v2 in p2.items():
p3[k1+k2] = p3.get(k1+k2, 0) + v1*v2
return p3
Exercise 2 can be done in a more Pythonic way by using set union for keys and dict comprehension for sums:
def somme_polynomes(p1, p2):
return {p: p1.get(p, 0) + p2.get(p, 0) for p in p1.keys() | p2.keys()}
Exercise 3 on the other hand is best done using nested loops aggregating products to the sum of keys, which is what you are already doing. The only slight enhancement I would make is to use the setdefault method to avoid the if statement:
def produit_polynomes(p1,p2):
p3 = {}
for key,value in p1.items():
for k,v in p2.items():
p3[key + k] = p3.get(key + k, 0) + value * v
return p3

Randomizing two lists and maintaining order in Python 3.4

I'm basically asking the exact same question as was asked here, but for Python 3.4.0.
In 3.4.0, this code:
a = ["Spears", "Adele", "NDubz", "Nicole", "Cristina"]
b = [1, 2, 3, 4, 5]
combined = zip(a, b)
random.shuffle(combined)
a[:], b[:] = zip(*combined)
does not work. What is the correct way to do this in 3.4.0?
In python 3, zip returns a zip object (i.e. it's itertools.izip from python 2).
You need to force it to materialize the list:
combined = list(zip(a, b))
If memory was tight, you can write your own shuffle function to avoid the need to create the zipped list. The one from Python is not very complicated
def shuffle(self, x, random=None, int=int):
"""x, random=random.random -> shuffle list x in place; return None.
Optional arg random is a 0-argument function returning a random
float in [0.0, 1.0); by default, the standard random.random.
Do not supply the 'int' argument.
"""
randbelow = self._randbelow
for i in reversed(range(1, len(args[0]))):
# pick an element in x[:i+1] with which to exchange x[i]
j = randbelow(i+1) if random is None else int(random() * (i+1))
x[i], x[j] = x[j], x[i]
Your function could be this:
def shuffle2(a, b):
for i in reversed(range(1, len(a))):
j = int(random.random() * (i+1))
a[i], a[j] = a[j], a[i]
b[i], b[j] = b[j], b[i]
To shuffle an arbitrary number of lists in unison
def shuffle_many(*args):
for i in reversed(range(1, len(args[0]))):
j = int(random.random() * (i+1))
for x in args:
x[i], x[j] = x[j], x[i]
eg
>>> import random
>>> def shuffle_many(*args):
... for i in reversed(range(1, len(args[0]))):
... j = int(random.random() * (i+1))
... for x in args:
... x[i], x[j] = x[j], x[i]
...
>>> a = ["Spears", "Adele", "NDubz", "Nicole", "Cristina"]
>>> b = [1, 2, 3, 4, 5]
>>> shuffle_many(a, b)
>>> a
['Adele', 'Spears', 'Nicole', 'NDubz', 'Cristina']
>>> b
[2, 1, 4, 3, 5]
Change combined = zip(a,b) to combined = list(zip(a,b)). You need a list, not an iterator, in order to shuffle in place.
In Python 3, zip returns an iterator rather than a list, so cast it to a list before shuffling it:
combined = list(zip(a, b))

Resources