Positive Count // Negative Sum - python

A fairly easy problem, but I'm still practicing iterating over multiple variables with for loops. In the below, I seek to return a new list, where x is the count of positive numbers and y is the sum of negative numbers from an input array arr.
If the input array is empty or null, I am to return an empty array.
Here's what I've got!
def count_positives_sum_negatives(arr):
return [] if not arr else [(count(x), sum(y)) for x, y in arr]
Currently receiving...
TypeError: 'int' object is not iterable

Simply use a sum comprehension
>>> arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -11, -12, -13, -14, -15]
>>> sum(1 for x in arr if x > 0)
10
>>> sum(x for x in arr if x < 0)
-65

wim's way is good. Numpy is good for these types of things too.
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -11, -12, -13, -14, -15])
print([arr[arr >= 0].size, arr[arr < 0].sum()])
>> [10, -65]

the error you get is from this part for x,y in arr that mean that arr is expected to be a list of tuples of 2 elements (or any similar container), like for example this [(1,2), (5,7), (7,9)] but what you have is a list of numbers, which don't contain anything else inside...
Now to get your desire result you can use the solution of wim, which need to iterate over the list twice or you can get it in one go with
>>> def fun(iterable):
if not iterable:
return []
pos = 0
neg = 0
for n in iterable:
if n>=0:
pos = pos + 1
else:
neg = neg + n
return [pos, neg]
>>> arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -11, -12, -13, -14, -15]
>>> fun(arr)
[10, -65]
>>>

Related

How to get the values in the dictionary into the list form and then square them into list

I have a dictionary of values in tuple form, how to get the values in list form.
I want to get values from the tuples and create new lists and create another 3 lists with squares from them.
dictionary={1:(1,2,3),2:(3,4,5),3:(6,7,8),4:(9,10,11),5:(12,13,14)}
s=list(d.values())
d=[item for t in s for item in t]
print(d)
I used list comprehension i got this output:
[1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
Using list comprehension
Expected_output:
[1,3,6,9,12],
[2,4,7,10,13],
[3,5,8,11,14],
squares**2 output above three list :
[1,9,36,81,144],
[4,16,49,100,169],
[9,25,64,121,196]
Provided with a Dictionary
First take a empty list and assign it to a variable “l”
Using list comprehension separate the values and store that in a variable
Iterate the values and append the empty list “l”
Now iterate the “l” using index values i[o], i[1], i[2] and store in various variables respectively
Using map function square the variables and store the values and print them using the list of variables
x = {
1:(1,2,3),
2:(4,5,6),
3:(7,8,9),
4:(10,11,12),
5:(13,14,15)
}
l = []
y = [i for i in x.values()]
for i in y:
l.append(i)
print(l)
m = [i[0] for i in l]
n = [i[1] for i in l]
o = [i[2] for i in l]
m1 = map(lambda i:i**2, m)
n1 = map(lambda i:i**2, n)
o1 = map(lambda i:i**2, o)
print(m)
print(list(m1))
print(n)
print(list(n1))
print(o)
print(list(o1))
you can use zip to collect the index elements of each list together, then use list comprehension to square them
dictionary={1:(1,2,3),2:(3,4,5),3:(6,7,8),4:(9,10,11),5:(12,13,14)}
list_vals = list(zip(*dictionary.values()))
squares = [[num ** 2 for num in nums] for nums in list_vals]
print(list_vals)
print(squares)
OUTPUT
[(1, 3, 6, 9, 12), (2, 4, 7, 10, 13), (3, 5, 8, 11, 14)]
[[1, 9, 36, 81, 144], [4, 16, 49, 100, 169], [9, 25, 64, 121, 196]]
Thanks to comments from #roganjosh highlighting that the dict will only be assured to be ordered if the pythong version is 3.6 or higher. If your python version is less than that you would first need to sort the values by order of the keys. Below is an example.
dictionary={2:(3,4,5),3:(6,7,8),4:(9,10,11),5:(12,13,14),1:(1,2,3)}
ordered_key_val = sorted(dictionary.items(), key=lambda items: items[0])
list_vals = list(zip(*[val for key, val in ordered_key_val]))
squares = [[num ** 2 for num in nums] for nums in list_vals]
print(list_vals)
print(squares)
You can use numpy to transpose the entire list once the values of the dictionary are obtained. You can use the below program
import numpy as np
dictionary={1:(1,2,3),2:(3,4,5),3:(6,7,8),4:(9,10,11),5:(12,13,14)}
list_out= []
for i in dictionary.keys():
list_out.append(dictionary[i])
tran_list = np.transpose(list_out)
out_list = tran_list*tran_list
Output of this is:
>>> out_list
array([[ 1, 9, 36, 81, 144],
[ 4, 16, 49, 100, 169],
[ 9, 25, 64, 121, 196]])
This is an array output! Anyway if you want it only in the list, ofcourse , you can play with it!
You can do this way:
>>> temp = list(zip(*dictionary.values()))
>>> [list(i) for i in temp]
[[1, 3, 6, 9, 12], [2, 4, 7, 10, 13], [3, 5, 8, 11, 14]]
>>> [[i**2 for i in elem] for elem in temp]
[[1, 9, 36, 81, 144], [4, 16, 49, 100, 169], [9, 25, 64, 121, 196]]
I have one dictionary here
d={1:(1,2,3),2:(4,5,6),3:(7,8,9),4:(10,11,12),5:(13,14,15)}
first I want to get values in tuple in three lists then I used list comprehension here The below code gives the tuple values in three lists
myList1 = [d [i][0] for i in (d.keys()) ]
print(myList1)
myList2 = [d [i][1] for i in (d.keys()) ]
print(myList2)
myList3 = [d [i][2] for i in (d.keys()) ]
print(myList3)
Here all the tuple values converted into list form
[1, 4, 7, 10, 13]
[2, 5, 8, 11, 14]
[3, 6, 9, 12, 15]
Now I want to squares the elements in three lists here I Used lambda expression the below code squares the elements in the lists
a1= list(map(lambda x: x**2 ,myList1))
print(a1)
a2= list(map(lambda x: x**2 ,myList2))
print(a2)
a3= list(map(lambda x: x**2 ,myList3))
print(a3)
The output is:
[1, 16, 49, 100, 169]
[4, 25, 64, 121, 196]
[9, 36, 81, 144, 225]

Python array logic

I am trying to create a list of lists with the input of m and n, where m is the number of lists within the main list and n is the number of elements within each given list. The grid should contain the integers from start to start + rows * cols - 1 and be ascending. But, every odd numbered row should be descending instead.
The code I've written is returning the expected results, but my automated tester is saying it's incorrect. Maybe my logic is messed up somewhere?
inputs:
start = 1, m = 3, n = 5
expected:
[[1,2,3,4,5],[10,9,8,7,6],[11,12,13,14,15]]
result = []
mylist = []
start = 1
for x in range(0, rows):
for x in range(0, cols):
result.append(start)
start += 1
for y in range(0, rows):
if y%2 != 0:
mylist.append(result[cols - 1::-1])
del result[cols - 1::-1]
else:
mylist.append(result[0:cols])
del result[0:cols]
return mylist
One possible solution, using itertools.count:
from itertools import count
def build(m, n, start=1):
lst, c = [], count(start)
for i in range(m):
lst.append([next(c) for j in range(n)][::-1] if i % 2 else [next(c) for j in range(n)])
return lst
print(build(3, 5, 1))
Prints:
[[1, 2, 3, 4, 5], [10, 9, 8, 7, 6], [11, 12, 13, 14, 15]]
print(build(3, 0, 1))
Prints:
[[], [], []]
just generate the list of numbers you need which will be n * m, in your case that would generate 0 to 14 in the python range function. However as we want to start at ` then we need to add the start offset too the range end.
Now we can generate all the numbers we need we just need to think about how to create them.
well we can add numbers to the list until the list reaches the size of n, then we need to start a new list, However if the list we just finished is an even numbered row then we need to reverse that list.
def build_lists(m, n, start=1):
data =[[]]
for i in range(start, n * m + start):
if len(data[-1]) < n:
data[-1].append(i)
else:
if len(data) % 2 == 0:
data[-1] = data[-1][::-1]
data.append([i])
if len(data) % 2 == 0:
data[-1] = data[-1][::-1]
return data
print(build_lists(3, 5))
print(build_lists(6, 3))
print(build_lists(6, 2, 100))
OUTPUT
[[1, 2, 3, 4, 5], [10, 9, 8, 7, 6], [11, 12, 13, 14, 15]]
[[1, 2, 3], [6, 5, 4], [7, 8, 9], [12, 11, 10], [13, 14, 15], [18, 17, 16]]
[[100, 101], [103, 102], [104, 105], [107, 106], [108, 109], [111, 110]]

Python with numpy: How to delete an element from each row of a 2-D array according to a specific index

Say I have a 2-D numpy array A of size 20 x 10.
I also have an array of length 20, del_ind.
I want to delete an element from each row of A according to del_ind, to get a resultant array of size 20 x 9.
How can I do this?
I looked into np.delete with a specified axis = 1, but this only deletes element from the same position for each row.
Thanks for the help
You will probably have to build a new array.
Fortunately you can avoid python loops for this task, using fancy indexing:
h, w = 20, 10
A = np.arange(h*w).reshape(h, w)
del_ind = np.random.randint(0, w, size=h)
mask = np.ones((h,w), dtype=bool)
mask[range(h), del_ind] = False
A_ = A[mask].reshape(h, w-1)
Demo with a smaller dataset:
>>> h, w = 5, 4
>>> %paste
A = np.arange(h*w).reshape(h, w)
del_ind = np.random.randint(0, w, size=h)
mask = np.ones((h,w), dtype=bool)
mask[range(h), del_ind] = False
A_ = A[mask].reshape(h, w-1)
## -- End pasted text --
>>> A
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19]])
>>> del_ind
array([2, 2, 1, 1, 0])
>>> A_
array([[ 0, 1, 3],
[ 4, 5, 7],
[ 8, 10, 11],
[12, 14, 15],
[17, 18, 19]])
Numpy isn't known for inplace edits; it's mainly intended for statically sized matrices. For that reason, I'd recommend doing this by copying the intended elements to a new array.
Assuming that it's sufficient to delete one column from every row:
def remove_indices(arr, indices):
result = np.empty((arr.shape[0], arr.shape[1] - 1))
for i, (delete_index, row) in enumerate(zip(indices, arr)):
result[i] = np.delete(row, delete_index)
return result

How can I check if one two-dimensional NumPy array contains a specific pattern of values inside it?

I have a large NumPy.array field_array and a smaller array match_array, both consisting of int values. Using the following example, how can I check if any match_array-shaped segment of field_array contains values that exactly correspond to the ones in match_array?
import numpy
raw_field = ( 24, 25, 26, 27, 28, 29, 30, 31, 23, \
33, 34, 35, 36, 37, 38, 39, 40, 32, \
-39, -38, -37, -36, -35, -34, -33, -32, -40, \
-30, -29, -28, -27, -26, -25, -24, -23, -31, \
-21, -20, -19, -18, -17, -16, -15, -14, -22, \
-12, -11, -10, -9, -8, -7, -6, -5, -13, \
-3, -2, -1, 0, 1, 2, 3, 4, -4, \
6, 7, 8, 4, 5, 6, 7, 13, 5, \
15, 16, 17, 8, 9, 10, 11, 22, 14)
field_array = numpy.array(raw_field, int).reshape(9,9)
match_array = numpy.arange(12).reshape(3,4)
These examples ought to return True since the pattern described by match_array aligns over [6:9,3:7].
Approach #1
This approach derives from a solution to Implement Matlab's im2col 'sliding' in python that was designed to rearrange sliding blocks from a 2D array into columns. Thus, to solve our case here, those sliding blocks from field_array could be stacked as columns and compared against column vector version of match_array.
Here's a formal definition of the function for the rearrangement/stacking -
def im2col(A,BLKSZ):
# Parameters
M,N = A.shape
col_extent = N - BLKSZ[1] + 1
row_extent = M - BLKSZ[0] + 1
# Get Starting block indices
start_idx = np.arange(BLKSZ[0])[:,None]*N + np.arange(BLKSZ[1])
# Get offsetted indices across the height and width of input array
offset_idx = np.arange(row_extent)[:,None]*N + np.arange(col_extent)
# Get all actual indices & index into input array for final output
return np.take (A,start_idx.ravel()[:,None] + offset_idx.ravel())
To solve our case, here's the implementation based on im2col -
# Get sliding blocks of shape same as match_array from field_array into columns
# Then, compare them with a column vector version of match array.
col_match = im2col(field_array,match_array.shape) == match_array.ravel()[:,None]
# Shape of output array that has field_array compared against a sliding match_array
out_shape = np.asarray(field_array.shape) - np.asarray(match_array.shape) + 1
# Now, see if all elements in a column are ONES and reshape to out_shape.
# Finally, find the position of TRUE indices
R,C = np.where(col_match.all(0).reshape(out_shape))
The output for the given sample in the question would be -
In [151]: R,C
Out[151]: (array([6]), array([3]))
Approach #2
Given that opencv already has template matching function that does square of differences, you can employ that and look for zero differences, which would be your matching positions. So, if you have access to cv2 (opencv module), the implementation would look something like this -
import cv2
from cv2 import matchTemplate as cv2m
M = cv2m(field_array.astype('uint8'),match_array.astype('uint8'),cv2.TM_SQDIFF)
R,C = np.where(M==0)
giving us -
In [204]: R,C
Out[204]: (array([6]), array([3]))
Benchmarking
This section compares runtimes for all the approaches suggested to solve the question. The credit for the various methods listed in this section goes to their contributors.
Method definitions -
def seek_array(search_in, search_for, return_coords = False):
si_x, si_y = search_in.shape
sf_x, sf_y = search_for.shape
for y in xrange(si_y-sf_y+1):
for x in xrange(si_x-sf_x+1):
if numpy.array_equal(search_for, search_in[x:x+sf_x, y:y+sf_y]):
return (x,y) if return_coords else True
return None if return_coords else False
def skimage_based(field_array,match_array):
windows = view_as_windows(field_array, match_array.shape)
return (windows == match_array).all(axis=(2,3)).nonzero()
def im2col_based(field_array,match_array):
col_match = im2col(field_array,match_array.shape)==match_array.ravel()[:,None]
out_shape = np.asarray(field_array.shape) - np.asarray(match_array.shape) + 1
return np.where(col_match.all(0).reshape(out_shape))
def cv2_based(field_array,match_array):
M = cv2m(field_array.astype('uint8'),match_array.astype('uint8'),cv2.TM_SQDIFF)
return np.where(M==0)
Runtime tests -
Case # 1 (Sample data from question):
In [11]: field_array
Out[11]:
array([[ 24, 25, 26, 27, 28, 29, 30, 31, 23],
[ 33, 34, 35, 36, 37, 38, 39, 40, 32],
[-39, -38, -37, -36, -35, -34, -33, -32, -40],
[-30, -29, -28, -27, -26, -25, -24, -23, -31],
[-21, -20, -19, -18, -17, -16, -15, -14, -22],
[-12, -11, -10, -9, -8, -7, -6, -5, -13],
[ -3, -2, -1, 0, 1, 2, 3, 4, -4],
[ 6, 7, 8, 4, 5, 6, 7, 13, 5],
[ 15, 16, 17, 8, 9, 10, 11, 22, 14]])
In [12]: match_array
Out[12]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [13]: %timeit seek_array(field_array, match_array, return_coords = False)
1000 loops, best of 3: 465 µs per loop
In [14]: %timeit skimage_based(field_array,match_array)
10000 loops, best of 3: 97.9 µs per loop
In [15]: %timeit im2col_based(field_array,match_array)
10000 loops, best of 3: 74.3 µs per loop
In [16]: %timeit cv2_based(field_array,match_array)
10000 loops, best of 3: 30 µs per loop
Case #2 (Bigger random data):
In [17]: field_array = np.random.randint(0,4,(256,256))
In [18]: match_array = field_array[100:116,100:116].copy()
In [19]: %timeit seek_array(field_array, match_array, return_coords = False)
1 loops, best of 3: 400 ms per loop
In [20]: %timeit skimage_based(field_array,match_array)
10 loops, best of 3: 54.3 ms per loop
In [21]: %timeit im2col_based(field_array,match_array)
10 loops, best of 3: 125 ms per loop
In [22]: %timeit cv2_based(field_array,match_array)
100 loops, best of 3: 4.08 ms per loop
There's no such search function built in to NumPy, but it is certainly possible to do in NumPy
As long as your arrays are not too massive*, you could use a rolling window approach:
from skimage.util import view_as_windows
windows = view_as_windows(field_array, match_array.shape)
The function view_as_windows is written purely in NumPy so if you don't have skimage you can always copy the code from here.
Then to see if the sub-array appears in the larger array, you can write:
>>> (windows == match_array).all(axis=(2,3)).any()
True
To find the indices of where the top-left corner of the sub-array matches, you can write:
>>> (windows == match_array).all(axis=(2,3)).nonzero()
(array([6]), array([3]))
This approach should also work for arrays of higher dimensions.
*although the array windows takes up no additional memory (only the strides and shape are changed to create a new view of the data), writing windows == match_array creates a boolean array of size (7, 6, 3, 4) which is 504 bytes of memory. If you're working with very large arrays, this approach might not be feasible.
One solution is to search the entire search_in array block-at-a-time (a 'block' being a search_for-shaped slice) until either a matching segment is found or the search_for array is exhausted. I can use it to get coordinates for the matching block, or just a bool result by sending True or False for the return_coords optional argument...
def seek_array(search_in, search_for, return_coords = False):
"""Searches for a contiguous instance of a 2d array `search_for` within a larger `search_in` 2d array.
If the optional argument return_coords is True, the xy coordinates of the zeroeth value of the first matching segment of search_in will be returned, or None if there is no matching segment.
If return_coords is False, a boolean will be returned.
* Both arrays must be sent as two-dimensional!"""
si_x, si_y = search_in.shape
sf_x, sf_y = search_for.shape
for y in xrange(si_y-sf_y+1):
for x in xrange(si_x-sf_x+1):
if numpy.array_equal(search_for, search_in[x:x+sf_x, y:y+sf_y]):
return (x,y) if return_coords else True # don't forget that coordinates are transposed when viewing NumPy arrays!
return None if return_coords else False
I wonder if NumPy doesn't already have a function that can do the same thing, though...
To add to the answers already posted, I'd like to add one that takes into account errors due to floating point precision in case that matrices come from, let's say, image processing for instance, where numbers are subject to floating point operations.
You can recurse the indexes of the larger matrix, searching for the smaller matrix. Then you can extract a submatrix of the larger matrix matching the size of the smaller matrix.
You have a match if the contents of both, the submatrix of 'large' and the 'small' matrix match.
The following example shows how to return the first indexes of the location in the large matrix found to match. It would be trivial to extend this function to return an array of locations found to match if that's the intent.
import numpy as np
def find_submatrix(a, b):
""" Searches the first instance at which 'b' is a submatrix of 'a', iterates
rows first. Returns the indexes of a at which 'b' was found, or None if
'b' is not contained within 'a'"""
a_rows=a.shape[0]
a_cols=a.shape[1]
b_rows=b.shape[0]
b_cols=b.shape[1]
row_diff = a_rows - b_rows
col_diff = a_cols - b_cols
for idx_row in np.arange(row_diff):
for idx_col in np.arange(col_diff):
row_indexes = [idx + idx_row for idx in np.arange(b_rows)]
col_indexes = [idx + idx_col for idx in np.arange(b_cols)]
submatrix_indexes = np.ix_(row_indexes, col_indexes)
a_submatrix = a[submatrix_indexes]
are_equal = np.allclose(a_submatrix, b) # allclose is used for floating point numbers, if they
# are close while comparing, they are considered equal.
# Useful if your matrices come from operations that produce
# floating point numbers.
# You might want to fine tune the parameters to allclose()
if (are_equal):
return[idx_col, idx_row]
return None
Using the function above you can run the following example:
large_mtx = np.array([[1, 2, 3, 7, 4, 2, 6],
[4, 5, 6, 2, 1, 3, 11],
[10, 4, 2, 1, 3, 7, 6],
[4, 2, 1, 3, 7, 6, -3],
[5, 6, 2, 1, 3, 11, -1],
[0, 0, -1, 5, 4, -1, 2],
[10, 4, 2, 1, 3, 7, 6],
[10, 4, 2, 1, 3, 7, 6]
])
# Example 1: An intersection at column 2 and row 1 of large_mtx
small_mtx_1 = np.array([[4, 2], [2,1]])
intersect = find_submatrix(large_mtx, small_mtx_1)
print "Example 1, intersection (col,row): " + str(intersect)
# Example 2: No intersection
small_mtx_2 = np.array([[-14, 2], [2,1]])
intersect = find_submatrix(large_mtx, small_mtx_2)
print "Example 2, intersection (col,row): " + str(intersect)
Which would print:
Example 1, intersection: [1, 2]
Example 2, intersection: None
Here's a solution using the as_strided() function from stride_tricks module
import numpy as np
from numpy.lib.stride_tricks import as_strided
# field_array (I modified it to have two matching arrays)
A = np.array([[ 24, 25, 26, 27, 28, 29, 30, 31, 23],
[ 33, 0, 1, 2, 3, 38, 39, 40, 32],
[-39, 4, 5, 6, 7, -34, -33, -32, -40],
[-30, 8, 9, 10, 11, -25, -24, -23, -31],
[-21, -20, -19, -18, -17, -16, -15, -14, -22],
[-12, -11, -10, -9, -8, -7, -6, -5, -13],
[ -3, -2, -1, 0, 1, 2, 3, 4, -4],
[ 6, 7, 8, 4, 5, 6, 7, 13, 5],
[ 15, 16, 17, 8, 9, 10, 11, 22, 14]])
# match_array
B = np.arange(12).reshape(3,4)
# Window view of A
A_w = as_strided(A, shape=(A.shape[0] - B.shape[0] + 1,
A.shape[1] - B.shape[1] + 1,
B.shape[0], B.shape[1]),
strides=2*A.strides).reshape(-1, B.shape[0], B.shape[1])
match = (A_w == B).all(axis=(1,2))
We can also find the indices of the first element of each matching block in A
where = np.where(match)[0]
ind_flat = where + (B.shape[1] - 1)*(np.floor(where/(A.shape[1] - B.shape[1] + 1)).astype(int))
ind = [tuple(row) for row in np.array(np.unravel_index(ind_flat, A.shape)).T]
Result
print(match.any())
True
print(ind)
[(1, 1), (6, 3)]

Sorting bytes in words, tuples in python

I looked around and I can't seem to find the proper way of sorting a 32 entry tuple by inverting every odd and even entry.
ex:
1 0 3 2 5 4 7 6 9 8
to
0 1 2 3 4 5 6 7 8 9
My current code looks like this
i=0
nd = []
while i < len(self.r.ipDeviceName):
print(i)
if i%2:
nd[i]=self.r.ipDeviceName[i-1]
else:
nd[i]=self.r.ipDeviceName[i+1]
dn = "".join(map(chr,nd))
devicenameText.SetValue(dn)
the type of self.r.ipDeviceName is tuple and I either get a IndexError or a tuple doesn't suport assignation depending on variations of the code
I also tried this with the same results
nd = self.r.ipDeviceName
for i in nd:
if i&0x01:
nd[i]=self.r.ipDeviceName[i-1]
else:
nd[i]=self.r.ipDeviceName[i+1]
dn = "".join(map(chr,nd))
devicenameText.SetValue(dn)
With the same results. Something very simple seems to elude me. Thanks for your help and time.
Tuples are immutable - you can't modify them once they are created. To modify individual elements you want to store the data in a mutable collection such as a list instead. You can use the built-in functions list and tuple to convert from tuple to list or vice versa.
Alternatively you could use zip and a functional style approach to create a new tuple from your existing tuple without modifying the original:
>>> t = tuple(range(10))
>>> tuple(x for i in zip(t[1::2], t[::2]) for x in i)
(1, 0, 3, 2, 5, 4, 7, 6, 9, 8)
Or using itertools.chain:
>>> import itertools
>>> tuple(itertools.chain(*zip(t[1::2], t[::2])))
(1, 0, 3, 2, 5, 4, 7, 6, 9, 8)
Note that the use of zip here assumes that your tuple has an even number of elements (which is the case here, according to your question).
You can't change a tuple, they're immutable. However you can replace them with a new one arranged the way you want (I wouldn't call what you want "sorted"). To do it, all that is needed it to swap each pair of items that are in the original tuple.
Here's a straight-forward implementation. Note it leaves the last entry alone if there are an odd number of them since you never said how you wanted that case handled. Dealing with that possibility complicates the code slightly.
def swap_even_odd_entries(seq):
tmp = list(seq)+[seq[-1]] # convert sequence to mutable list and dup last
for i in xrange(0, len(seq), 2):
tmp[i],tmp[i+1] = tmp[i+1],tmp[i] # swap each entry with following one
return tuple(tmp[:len(seq)]) # remove any excess
a = (1, 0, 3, 2, 5, 4, 7, 6, 9, 8)
a = swap_even_odd_entries(a)
b = (91, 70, 23, 42, 75, 14, 87, 36, 19, 80)
b = swap_even_odd_entries(b)
c = (1, 0, 3, 2, 5)
c = swap_even_odd_entries(c)
print a
print b
print c
# output
# (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
# (70, 91, 42, 23, 14, 75, 36, 87, 80, 19)
# (0, 1, 2, 3, 5)
The same thing can also be done in a less-readable way as a long single expression. Again the last entry remains unchanged if the length is odd.
swap_even_odd_entries2 = lambda t: tuple(
v for p in [(b,a) for a,b in zip(*[iter(t)]*2) + [(t[-1],)*2]]
for v in p)[:len(t)]
a = (1, 0, 3, 2, 5, 4, 7, 6, 9, 8)
a = swap_even_odd_entries2(a)
b = (91, 70, 23, 42, 75, 14, 87, 36, 19, 80)
b = swap_even_odd_entries2(b)
c = (1, 0, 3, 2, 5)
c = swap_even_odd_entries2(c)
print
print a
print b
print c
# output
# (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
# (70, 91, 42, 23, 14, 75, 36, 87, 80, 19)
# (0, 1, 2, 3, 5)
If you add the functions grouper and flatten (see itertools recipes) to your toolset, you can do:
xs = [1, 0, 3, 2, 5, 4, 7, 6, 9, 8]
xs2 = flatten((y, x) for (x, y) in grouper(2, xs))
# list(xs2) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
You could even write flatten(imap(reversed, grouper(2, xs)) but I guess only die-hard functional guys would like it.

Categories