Python : how to make 1-D array from 2-D array - python

I have array2D = [[1,2,3],[4,5,6]]. What I want is a function which takes an index and returns the elements in 1D array.
Example: fn(0) -> returns [1,4]
fn{1) -> returns [2,5]
I need a fast way to do this.

you can use lambda and list comprehension:
array2D = [[1,2,3],[4,5,6]]
fn = lambda x: [item[x] for item in array2D]
print(fn(0)) # [1, 4]
print(fn(1)) # [2, 5]
print(fn(2)) # [3, 6]
as suggested in the comments, you may apply the same concept with a function definition:
def fn(x): return [item[x] for item in array2D]
print(fn(0)) # [1, 4]
print(fn(1)) # [2, 5]
print(fn(2)) # [3, 6]
Lambda functions are pretty useful, and let you define operation in a really clear way.
In our example, our lambda accept a variable x, which represent the index we want of each item in array2D
Then you have list comprehension, similarly to lambda function, they are a really powerful tool and a must in python
In this situation you should prefear the function definiton, as suggested by PEP-8.

The following list comprehension will work:
def fn(i, lst):
return [sublst[i] for sublst in lst]
>>> array2D = [[1, 2, 3], [4, 5, 6]]
>>> fn(0, array2D)
[1, 4]
>>> fn(1, array2D)
[2, 5]

You can use operator.itemgetter:
array2D = [[1,2,3],[4,5,6]]
from operator import itemgetter
def fn(x, k):
return list(map(itemgetter(k), x))
fn(array2D, 0) # [1, 4]
If you want to define new functions for retrieving a specific index, you can do so via functools.partial:
from functools import partial
def fn(x, k):
return list(map(itemgetter(k), x))
get_zero_index = partial(fn, k=0)
get_zero_index(array2D) # [1, 4]

Here are my two cents using slicing (I have to use additional np.array() for this because your original data was a list):
array2D = np.array([[1,2,3],[4,5,6]])
def fn(n): return (list(array2D[:,n]))
print (fn(0), fn(1), fn(2))

How about a generator?
We could use zip to pack them, then create a empty list to store the generated data:
class myZip(object):
__slots__ = ('zipData', 'interList')
def __init__(self, *args):
self.zipData = zip(*args)
self.interList = []
def __call__(self, index):
try:
return self.interList[index]
except IndexError:
try:
if index == 0:
self.interList.append(next(self.zipData))
return self.interList[index]
for i in range(index-(len(self.interList)-1)):
self.interList.append(next(self.zipData))
return self.interList[index]
except StopIteration:
raise IndexError("index out of range")
def __iter__(self):
for i in self.interList:
yield i
for i in self.zipData:
yield i
array2D = [[1,2,3],[4,5,6]]
a = myZip(*array2D)
print(a(2))
print(a(1))
print(a(0))
---
(3, 6)
(2, 5)
(1, 4)
The benefits of this is we do not need to produce all data at once.

Related

How do I write instructions inside map in Python?

The input is a list: ["asdf", "ghjk", "lmnop", "zyx"].
I specifically need to apply a function over all elements of the list (so, I should use map). The function should do something like f(x, k) = x + k, where k is a constant equal to 1, and x is a counter (hence, x will be 0 at first, and when gradually iterating over the list, it should increment somehow, but how?).
Assuming these, the expected output should be: [1, 2, 3, 4].
from functools import partial
def f(x, k):
return x + k
def with_partial(my_list, k=1):
func = partial(f, k=k) # partial object which acts as a function when called
x = 0 # counter
# map -> applies a funciton over all elements of my_list
rez = list(map(lambda it: (func(x)), my_list))
# if I want to increment it, it should look like: rez = list(map(lambda it: x = (func(x)), my_list))
print("Result with partial: " + str(rez))
if __name__ == "__main__":
with_partial([5, 6, 7])
with_partial(["asdf", "ghjk", "lmnop", "zyx"])
But the output is:
Result with partial: [1, 1, 1]
Result with partial: [1, 1, 1, 1]
But I want it to be like:
Result with partial: [1, 2, 3]
Result with partial: [1, 2, 3, 4]
I can use list comprehension quite easily for that type of problem, but I need to understand how can I write instructions inside map, so that I can increment my counter effectively. Or... Is there anything about partial function I should know already, but I don't? How do I solve this type of problem using functional programming?

What makes function composition so powerful?

I have read about functional programming and it's core concept seem to be the high use of immutable datastructures, which in turn lead to pure functions. These pure functions ought be easily composable like in this example:
def add(x, y):
return 3 + 3
def minus1(z):
return z - 1
print(minus1(add(3,3)))
I understand, that i can compose functions and build new functions by this method. In the following example, i have composed two impure functions:
def add_list_last(arr):
arr.append(1)
return arr
def insert_entry_at_start(arr):
arr.insert(0,1)
return arr
print(add_list_last(insert_entry_at_start([1,2,3])))
Why can't i just compose impure functions like i did in the second example and why is it so powerful? What is the disadvantage of the second example and why is the mutation of the array preventing composability?
T = TypeVar('T')
def split(v: T) -> Tuple[T, T]:
return v, v
def apply_to_first(f: Callable[[T], T], *xs: T) -> Sequence[T]:
return (f(xs[0]), *xs[1:])
apply_to_first(insert_entry_at_start, *split([1, 2]))
You would expect this result here:
([1, 2, 1], [1, 2])
But in fact you get:
([1, 2, 1], [1, 2, 1])
Because insert_entry_at_start is impure.
Here's a simplified example:
def prepend_impure(lst, element):
lst.insert(0, element)
return lst
def prepend_pure(lst, element):
cpy = lst[:]
cpy.insert(0, element)
return cpy
def combine(list1, list2):
return [i + j for i, j in zip(list1, list2)]
list1 = [2, 3, 4]
list2 = [2, 3, 4]
print(combine(prepend_impure(list1, 1), prepend_impure(list1, 1)))
print(combine(prepend_pure(list2, 1), prepend_pure(list2, 1)))
Output:
[2, 2, 4, 6, 8]
[2, 4, 6, 8]
The prepend functions insert an element at the beginning of a given list and return the list.
The combine function adds 2 lists together.
Personally, I would expect the code to both return lists of [1, 2, 3, 4] and add the 2 together.
Not only do you not get what you expect with the impure version.. In a large codebase, trying to be mindful of all the ways a function can mutate or modify your objects can produce a lot of cognitive overload.

How to add an array with using Python

Given an array a = [1,2,3,[4,5]] using Python 3, how can I add all the elements in the array?
sum(a[0])
sun(a[0][1])
The above code did not work. Also in the case, if you're given an array with an unknown amount of arrays inside arrays, How can those numbers be calculated?
def xsum(x):
if not x:
return 0
head, *tail = x
if isinstance(head, list):
return xsum(head) + xsum(tail)
elif tail:
return head + xsum(tail)
else:
return head
You need a flatten function. Here is one:
def flatten(a):
"""generator of flattened n-deep iterable (excluding str) a."""
for elem in a:
if not isinstance(elem, str):
try:
yield from flatten(elem)
except TypeError:
yield elem
else:
yield elem
which you can then use in sum, for example:
a = [1, 2, 3, [4, [5, 6]]
print(list(flatten(a))) # --> [1, 2, 3, 4, 5, 6]
print(sum(flatten(a))) # --> 21
You can use functools.reduce to sum this nested list
>>> from functools import reduce
>>> a = [1,2,3,[4,5]]
>>> reduce(lambda x,y: x + (sum(y) if type(y)==list else y), [0]+a)
15
If the list can be more than one level nested, you have to use a recursive approach
>>> f = lambda x,y: x + (reduce(f, y) if type(y)==list else y)
>>> reduce(f, [0]+a)
15
You can use the closure property for finding sum of infinite nested list.
def nested_list_sum(x):
c = []
def l_sum(x):
for i in x:
if isinstance(i, list):
l_sum(i)
else:
c.append(i)
l_sum(x)
return sum(c)
like
a = [1,2,3,[4,5]] ----> 15
a = [1,2,3,[4,5, [6,7, [8, 9]]], [10, 11, 12, [13, 14, 5]]] -- > 110

How to random.shuffle slice or subset of list-of-objects?

Consider random.shuffle on a list-of-objects (i.e. not primitive types like int).
I'm talking about any general object; the numbers in the following are only an example.
Since random.shuffle acts in-place and returns None, that makes it slightly clunky to shuffle a slice or subset of that list.
For a slice (or subset), is there a better way than taking the slice (or a deepcopy), shuffling it, then overwriting the original list slice with the result? i.e. as follows:
import copy
import random
class SomeNumber(object): # just a stupid class to illustrate the point
def __init__(self, n):
self.n = n
def __repr__(self):
return str(self.n)
ll = [ SomeNumber(i) for i in range(6) ]
# [0, 1, 2, 3, 4, 5]
# Say we want to shuffle slice 2:end
#ld = copy.deepcopy(ll[2:])
ld = ll[2:]
# [2, 3, 4, 5]
random.shuffle(ld)
# [3, 2, 4, 5]
# ll didn't get shuffled though, so must overwrite the original slice...
ll[2:] = ld
# [0, 1, 5, 3, 4, 2]
del ld
Consider also the more general case where I might want to pass a boolean vector of which elements are to be included in the shuffle. Then extract that subset, shuffle it and reinsert it. Can you find a good idiom?
PS #ReutSharabani suggests something like:
def partial_shuffle(lst, imin, imax):
lst[imin:imax] = sorted(lst[imin:imax], key=lambda x: random.random())
return lst
as seen on: Why does random.shuffle return None?
#!/usr/bin/env python
import copy
import random
class SomeNumber(object): # just a stupid class to illustrate the point
def __init__(self, n):
self.n = n
def __repr__(self):
return str(self.n)
ll = [ SomeNumber(i) for i in range(6) ]
print ll
ll[2:] = sorted(ll[2:], key=lambda x: random.random())
print ll
It uses sorted to act as shuffle (sort according to a random key), but I'm not sure if it's really random.
You could create a shuffle function to return a shuffled copy of a list:
#!/usr/bin/env python
import random
# the function
def shuffle(ll, include_list=None):
if not include_list:
include_list = [True] * len(ll)
# tag the list (default: all, for use of slices) and shuffle
tagged_list = [x for x in zip(include_list, ll)]
shuffle_list = [x for x in tagged_list if x[0]]
random.shuffle(shuffle_list)
# re-insert to tagged list by false tags, enumerate indices changed
false_indices = sum(map(lambda x: [x[0]] if x[1][0] else [], enumerate(tagged_list)), [])
for shuffle_index, tagged_index in enumerate(false_indices):
tagged_list[tagged_index] = shuffle_list[shuffle_index]
# return only the values
return [x[1] for x in tagged_list]
# now use it whenever you want to shuffle something
l = [1, 2, 3, 4, 5]
print "shuffeled: %s" % shuffle(l)
# original list stays the same (not in-place)
print "original: %s" % l
# now to shuffle a part of a list you can:
print "shuffeled (2+): %s" % shuffle(l[2:])
# with a boolean filter list:
print "shuffle indices 0, 2, 4: %s " % shuffle(l, [True, False, True, False, True])

Using for loops to create multiple lists

Currently I have a function.
def create1(n):
output = []
for i in range(n):
output.append(int(i)+1)
return output
It returns [1,2,3] whenever enter create(3). However, I want it to return [[1],[1,2],[1,2,3]].
I know there's a problem with something in my for loop but I can't figure it out.
Use range() to create lists of numbers quickly:
def create1(n):
output = []
for i in range(n):
output.append(range(1, i + 2))
return output
or, using a list comprehension:
def create1(n):
return [range(1, i + 2) for i in range(n)]
If you are using Python 3, turn the iterator returned by range() into a list first:
for i in range(n):
output.append(list(range(1, i + 2)))
Quick demo:
>>> def create1(n):
... return [range(1, i + 2) for i in range(n)]
...
>>> create1(3)
[[1], [1, 2], [1, 2, 3]]
This works in Python 2 and Python 3:
>>> def create1(n):
... return [list(range(1,i+1)) for i in range(1,n+1)]
...
>>> create1(5)
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
Try this:
def create(n):
output = []
for i in range(n):
output.append(range(1,i+2))
return output
print create(3)
What you really want is a list appended at every stage. So try this
def create1(n):
output = []
for i in range(n):
output.append(range(1,i+2)) # append a list, not a number.
return output
range(n) gives you a list of integers from 0 to n-1. So, at each stage (at each i), you're appending to the output a list from 0 to i+1.

Categories