oneliner using pandas or numpy - python

How would I implment this code using pandas or numpy?
x =[]
for i in xrange(1,6):
x.append(i)
print x
combi = (list(itertools.permutations(x, 5)))
for i in combi:
print i
l = (i[0]) + abs(i[0] - i[1]) + abs(i[1] - i[2]) + abs(i[2] - i[3]) + abs(i[3] - i[4])
print l
output of combi is
(1, 2, 3, 4, 5)
(1, 2, 3, 5, 4)
(1, 2, 4, 3, 5)
(1, 2, 4, 5, 3)
I try to add the first number in each tuple to difference of second third 4th and fifth for each tuple which give me this
5
6
7
7
easier if increase my range to 10 i will have to write the code for variable l upto 10.

You are taking the diff between subsequent elements, and then summing the abs of those diffs and then you add to that, the first element in the sequence:
Code:
for i in it.permutations(xrange(1, 6), 5):
k = i[0] + sum(np.abs(np.diff(i)))
Test Code:
import numpy as np
import itertools as it
for i in it.permutations(xrange(1, 6), 5):
k = i[0] + sum(np.abs(np.diff(i)))
l = (i[0]) + abs(i[0] - i[1]) + abs(i[1] - i[2]) + abs(i[2] - i[3]) + abs(i[3] - i[4])
assert l == k

Related

Iterative summation

I'm trying to write a python code that allows me to iteratively sum up the average values of three elements of a list, starting with the third element and its two predecessors. Let me give you an example:
list = [1, 2, 3, 4, 5, 6, 7]
I want to calculate the following:
sum_of_average_values = sum(1, 2, 3)/3 + sum(2, 3, 4)/3 + sum(3, 4, 5)/3 + sum(4, 5, 6)/3 + sum(5, 6, 7)/3
Since I'm quite new to programming I couldn't find an effective way of putting this into a function.
You can do in this way:
a = [1,2,3,4,5,6,7]
sum_of_average_values = 0
for i in range(0,len(a)-2):
sum_of_average_values += sum(a[i:i+2])/3
print(sum_of_average_values)
You could use rolling from pandas.
import pandas as pd
num_list = [1, 2, 3, 4, 5, 6, 7]
average_sum = sum(pd.Series(num_list).rolling(3).mean().dropna())
print(average_sum)
Many ways to achieve this, one way is recursively.
The function averages the last three elements of a list and adds the result to the result generated by the function with a list lacking the last element. Continues like this until the list is shorter than 3.
def fn(l):
if len(l) < 3:
return 0
return sum(l[-3:])/3 + fn(l[:-1])
print(fn([1, 2, 3, 4, 5, 6, 7]))
Another solution where you can specify the amount of elements you want to sum up and average:
l = [1, 2, 3, 4, 5, 6, 7]
def sum_avg(l, n):
res = 0
for i in range(n-1, len(l)):
res += sum([l[j] for j in range(i, i-n, -1)])/n
return res
print(sum_avg(l, 3))
--> 20.0
Mathematically, this would could be obtain by averaging the sums of 3 sublists:
L = [1, 2, 3, 4, 5, 6, 7]
r = (sum(L) + sum(L[1:-1]) + sum(L[2:-2]))/3 # 20.0
and can be generalized to a window size of w:
w = 3
r = sum(sum(L[p:-p or None]) for p in range(w)) / w
It can also be implemented without the overhead of generating sublists by using item positions to determine the number of times they are added to the total:
r = sum(n*min(i+1,len(L)-i,w) for i,n in enumerate(L)) / w
This would be the most memory-efficient of the 3 methods because it use an iterator to feed data to the sum function and only goes through the data once.
Detailed explanation:
Since all the averages that are added together are a division by 3, we can produce the total sum and divide by 3 at the end
the number at the first and last positions are added once
the number at the second and penultimate positions are added twice
The numbers from the third position up to the antepenultimate will be added 3 times
visually:
(1 + 2 + 3) / 3
(2 + 3 + 4) / 3
(3 + 4 + 5) / 3
(4 + 5 + 6) / 3
(5 + 6 + 7) / 3
(1x1 + 2x2 + 3x3 + 4x3 + 5x3 + 6x2 + 7x1) / 3 = 20.0
n = 1 2 3 4 5 6 7 # value
* = 1 2 3 3 3 2 1 # multiplier (times added)
-------------------------
(2, 4, 9, 12, 15, 12, 7) / 3 = 20.0
i = 0 1 2 3 4 5 6 # index
1 2 3 3 3 2 1 # min(i+1,len(L)-i,w) = multiplier
You can do in one line using list comprehension as:
n = 3
avg = sum( [ sum(lst[i:i+n])/n for i in range(0, len(lst) - (n - 1)) ] )
print(avg) # 20.0

Proper way to write a complex objective and constraints in Python Pulp

I am following a book that explains advanced linear programs. So far it's been good, but this example has me stumped on how to properly write the objective functions and constraints.
Below you'll find my attempt at coding the left coefficient but as you can see it doesn't seem correct.
My implementation so far is as follow
import pulp as lp
# data
r = 0.02
T = 2
J = 3
E = 6
K = {(1): 2, (2): 3, (3): 4, (4): 4, (5): 6, (6): 7}
C_j_t = {(1, 1): 2, (1, 2): 3, (2, 1): 4, (2, 2): 5, (3, 1): 6, (3, 2): 7}
F_j_e = {(1, 1): 2, (1, 2): 3, (2, 1): 4, (2, 2): 5, (3, 1): 6, (3, 2): 7}
I_j_e = {(1, 1): 2, (1, 2): 3, (2, 1): 4, (2, 2): 5, (3, 1): 6, (3, 2): 7}
# F_j_e = 1
prob = lp.LpProblem('Foobar Village Highway Problem', lp.LpMaximize)
X_j_t = lp.LpVariable.dicts("X",
[(j, t)
for j in range(1, J)
for t in range(1, T)],
cat='Continuous')
coef_left = {}
for t in range(1, T):
for j in range(1, J):
coef_left[(j, t)] = (1 + r)**-t * C_j_t[(j, t)]
pv = (1 + r)**-T
right_side = {}
for j in range(1, J):
for e in range(1, K[(j)]):
right_side[(j, e)] = pv * F_j_e[(j, e)] * I_j_e[(j, e)]
prob += lp.lpSum(coef_left[(j, t)] * X_j_t[(j, t)] + right_side[(j, e)]
for j in range(1, J)
for t in range(1, T)
for e in range(1, E))
prob.writeLP(r'8.2.1.lp')
# Solve
prob.solve()
I don't think this is correct or that I'm using the right methodology to code this complex objective an constraint.
it returns the following:
\* Foobar_Village_Highway_Problem *\
Maximize
OBJ: 3.92156862745 X_(1,_1) + 7.8431372549 X_(2,_1)
Subject To
Bounds
X_(1,_1) free
X_(2,_1) free
End
We cannot reproduce much because the fragment is incomplete, but there seems an issue with the loop structure:
prob += lp.lpSum(coef_left[(j, t)] * X_j_t[(j, t)]
for j in range(1, J)
for i in range(1, T)) <----- for t

How can I do an operation only for one value on the fly in a numpy array? (or the better way)

I have an array e.g. a = np.arange(0, 5) and it looks like this array([0, 1, 2, 3, 4]).
Now what I want to do is e.g. to add only at the index 2 a value like 5 and get a new numpy array object.
Like this: b = add_value_on_idx(a, 2, 5) # This should return a new object
I have tried this approaches to achieve this
#! /usr/bin/python3.5
import numpy as np
from time import time
# Get a numpy array first
a = np.random.randint(0, 10, (5, ))
# Define a new tuple of zeros with only one number at the
# specific index
def do_add_at_idx_1(arr, idx, val):
return arr+(((0, )*(idx))+(val, )+(0, )*(arr.shape[0]-idx-1))
# Or do it like this
def do_add_at_idx_2(arr, idx, val):
one_val_vec = np.zeros((arr.shape[0], )).astype(arr.dtype)
one_val_vec[idx] = val
return arr+one_val_vec
# Or add it directly to the given array
def do_add_at_idx_3(arr, idx, val):
arr = arr.copy()
arr[idx] += val
return arr
# Or use directly np.add.at function (but need a copy first)
def do_add_at_idx_4(arr, idx, val):
arr = arr.copy()
np.add.at(arr, idx, val)
return arr
a_old = a.copy()
print("a: {}".format(a))
print("do_add_at_idx_1(a, 2, 5): {}".format(do_add_at_idx_1(a, 2, 5)))
print("do_add_at_idx_2(a, 2, 5): {}".format(do_add_at_idx_2(a, 2, 5)))
print("do_add_at_idx_3(a, 2, 5): {}".format(do_add_at_idx_3(a, 2, 5)))
print("do_add_at_idx_4(a, 2, 5): {}".format(do_add_at_idx_4(a, 2, 5)))
print("Was 'a' modified? {}".format("No." if np.all(a==a_old) else "YES!!"))
Which will output something like this:
a: [6 6 9 2 8]
do_add_at_idx_1(a, 2, 5): [ 6 6 14 2 8]
do_add_at_idx_2(a, 2, 5): [ 6 6 14 2 8]
do_add_at_idx_3(a, 2, 5): [ 6 6 14 2 8]
do_add_at_idx_4(a, 2, 5): [ 6 6 14 2 8]
Was 'a' modified? No.
I have also tested the time differences for these functions too:
n = 100000
print("n: {}".format(n))
idxs = np.random.randint(0, a.shape[0], (n, ))
vals = np.random.randint(0, 10, (n, ))
for i in range(1, 5):
func_name = "do_add_at_idx_{}".format(i)
func = globals()[func_name]
start = time()
for idx, val in zip(idxs, vals):
func(a, idx, val)
delta = time()-start
print("Taken time for func '{}': {:2.4f}s".format(func_name, delta))
With this as an output:
Taken time for func 'do_add_at_idx_1': 2.1899s
Taken time for func 'do_add_at_idx_2': 1.4600s
Taken time for func 'do_add_at_idx_3': 0.5757s
Taken time for func 'do_add_at_idx_4': 2.8043s
Are there any better ways to do this approach?

Number of pairs [duplicate]

I am trying to write a code that takes
m. a, a list of integers
n. b, an integer
and returns the number of pairs (m,n) with m,n in a such that |m-n|<=b.
So far, I've got this
def nearest_pairs(a, b):
m= []
n= int
num_pairs = 0
return num_pairs
def main():
# The nearest pairs are (1,2), (2,1), (2,5) and (5,2)
x = nearest_pairs( [1,2,5] , 3 )
print( "nearest_pairs([1, 2, 5], 3) = " , nearest_pairs([1, 2, 5], 3) )
# The nearest pairs are (1,2) and (2,1)
y = nearest_pairs( [1, 2, 5] , 2 )
print( "nearest_pairs([1, 2, 5], 2) = " , nearest_pairs([1, 2, 5], 2) )
if __name__ == '__main__':
main()
The desired output should look like
>>> nearest_pairs([1,2,5],3) = 4
where 4 is the number of close pairs according to the restrictions. However, I get an error. Could anyone lead me to the right direction?
Yours doesn't make sense. No idea what you're trying with len(a, b), but it's not even allowed, since len takes only one argument. And returning something just when you found the first counting pair? Here's a fix:
def close_pairs(l, d):
ctr = 0
for a,b in permutations(l, 2):
if (a - b) <= d and (b - a) <= d:
ctr += 1
return ctr
And here's how I'd do it:
def close_pairs(l, d):
return sum(abs(a-b) <= d for a, b in permutations(l, 2))
from itertools import permutations
def nearest_pairs(a, b):
for m, n in permutations(a, 2):
if abs(m - n) <= b:
yield (m, n)
>>> list(nearest_pairs([1, 2, 5], 3))
[(1, 2), (2, 1), (2, 5), (5, 2)]
>>> list(nearest_pairs([1, 2, 5], 2))
[(1, 2), (2, 1)]
If you just want the count:
def nearest_pairs_count(a, b):
c, l = 0, len(a)
for i in range(l):
for j in range(i + 1, l):
if abs(a[i] - a[j]) <= b:
c += 2
return c

Python, finding neighbors in a 2-d list

So here's the issue, I have a 2-d list of characters 'T' and 'F', and given coordinates I need to get all of its neighbors. I have this:
from itertools import product, starmap
x, y = (5, 5)
cells = starmap(lambda a, b: (x + a, y + b), product((0, -1, +1), (0, -1, +1)))
from determining neighbors of cell two dimensional list But it will only give me a list of coordinantes, so i still fetch the values afterwords. I'd like the search and retrieval done in one step, so findNeighbors(5,5) would return F,T,F,F,... instead of (5, 4), (5, 6), (4, 5), (4, 4)... Is there a fast way of doing this? The solutin can include a structure other than a list to hold the initial information
The following should work, with just a minor adaptation to the current code:
from itertools import product, starmap, islice
def findNeighbors(grid, x, y):
xi = (0, -1, 1) if 0 < x < len(grid) - 1 else ((0, -1) if x > 0 else (0, 1))
yi = (0, -1, 1) if 0 < y < len(grid[0]) - 1 else ((0, -1) if y > 0 else (0, 1))
return islice(starmap((lambda a, b: grid[x + a][y + b]), product(xi, yi)), 1, None)
For example:
>>> grid = [[ 0, 1, 2, 3],
... [ 4, 5, 6, 7],
... [ 8, 9, 10, 11],
... [12, 13, 14, 15]]
>>> list(findNeighbors(grid, 2, 1)) # find neighbors of 9
[8, 10, 5, 4, 6, 13, 12, 14]
>>> list(findNeighbors(grid, 3, 3)) # find neighbors of 15
[14, 11, 10]
For the sake of clarity, here is some equivalent code without all of the itertools magic:
def findNeighbors(grid, x, y):
if 0 < x < len(grid) - 1:
xi = (0, -1, 1) # this isn't first or last row, so we can look above and below
elif x > 0:
xi = (0, -1) # this is the last row, so we can only look above
else:
xi = (0, 1) # this is the first row, so we can only look below
# the following line accomplishes the same thing as the above code but for columns
yi = (0, -1, 1) if 0 < y < len(grid[0]) - 1 else ((0, -1) if y > 0 else (0, 1))
for a in xi:
for b in yi:
if a == b == 0: # this value is skipped using islice in the original code
continue
yield grid[x + a][y + b]

Categories

Resources