Python: Building a list comprehension that grows a list - python

I want to write some python that will take an existing list, and create a new list containing two entries for every entry in the original.
Example: Every entry should produce two new entries: x+1, and 3x.
a = [1]
a = [2, 3]
a = [3, 6, 4, 9]
a = [4, 9, 7, 18, 5, 12, 10, 27]
What code could be entered to produce the desired output:
a = [1]
for i in range(3):
a = ???
I have tried:
a = [(x+1, 3*x) for x in a]
... but this was incorrect because the first iteration gives a list containing a single tuple:
a = [(2, 3)]
... and a subsequent iteration does not work.
In addition to the answer, some explanation into the thought process that produces the answer would be most helpful.
EDIT: If anyone can give me some insight as to why my question is receiving close votes, I would appreciate that as well.

Here is a pretty efficient solution that uses itertools.chain.from_iterable and a generator expression:
>>> from itertools import chain
>>> a = [1]
>>> list(chain.from_iterable((x+1, x*3) for x in a))
[2, 3]
>>> a = [2, 3]
>>> list(chain.from_iterable((x+1, x*3) for x in a))
[3, 6, 4, 9]
>>> a = [3, 6, 4, 9]
>>> list(chain.from_iterable((x+1, x*3) for x in a))
[4, 9, 7, 18, 5, 12, 10, 27]
>>>
The links provided should explain everything except the list(...) part. I did that so the results were lists and not something like <itertools.chain object at 0x01815370>.
Edit in response to comment:
Yes, you can chain as many chain objects as you want and then convert the whole thing to a list in the end. See a demonstration below:
>>> a = [3, 6, 4, 9]
>>> list(chain.from_iterable((chain.from_iterable((x+1, x*3) for x in a), chain.from_iterable((x+1, x*3) for x in a))))
[4, 9, 7, 18, 5, 12, 10, 27, 4, 9, 7, 18, 5, 12, 10, 27]
>>>

def somefunc(n):
if not n:
return [1]
else:
return list(itertools.chain.from_iterable([(i+1, 3*i) for i in somefunc(n-1)]))
Output:
In [20]: somefunc(3)
Out[20]: [4, 9, 7, 18, 5, 12, 10, 27]
In [21]: somefunc(2)
Out[21]: [3, 6, 4, 9]
In [22]: somefunc(1)
Out[22]: [2, 3]

Related

Adding values from one array depending on occurrences of values in another array

Cant Really bend my mind around this problem I'm having:
say I have 2 arrays
A = [2, 7, 4, 3, 9, 4, 2, 6]
B = [1, 1, 1, 4, 4, 7, 7, 7]
what I'm trying to do is that if a value is repeated in array B (like how 1 is repeated 3 times), those corresponding values in array A are added up to be appended to another array (say C)
so C would look like (from above two arrays):
C = [13, 12, 12]
Also sidenote.. the application I'd be using this code for uses timestamps from a database acting as array B (so that once a day is passed, that value in the array obviously won't be repeated)
Any help is appreciated!!
Here is a solution without pandas, using only itertools groupby:
from itertools import groupby
C = [sum( a for a,_ in g) for _,g in groupby(zip(A,B),key = lambda x: x[1])]
yields:
[13, 12, 12]
I would use pandas for this
Say you put those arrays in a DataFrame. This does the job:
df = pd.DataFrame(
{
'A': [2, 7, 4, 3, 9, 4, 2, 6],
'B': [1, 1, 1, 4, 4, 7, 7, 7]
}
)
df.groupby('B').sum()
If you want pure python solution, you can use itertools.groupby:
from itertools import groupby
A = [2, 7, 4, 3, 9, 4, 2, 6]
B = [1, 1, 1, 4, 4, 7, 7, 7]
out = []
for _, g in groupby(zip(A, B), lambda k: k[1]):
out.append(sum(v for v, _ in g))
print(out)
Prints:
[13, 12, 12]

Removal of multiples of a number on a list:

I have a number two, I have this list, how would I go about removing multiples of the number 2 from that list and update it?
10 [2, 3, 4, 5, 6, 7, 8, 9, 10] 2
Assuming you have a list l and a number n you can remove all multiples of n from l with a list comprehension:
l = [i for i in l if i%n]
writing if i%n here is the same as writing if i%n != 0, and n divides i iff i%n==0
Methods:
Using a generator
One of the first option that comes to mind is to make use of a generator. The generator would iterate through a sequence, and test if the current element is divisible by n. This allows you to have a more generic solution as well:
def filter_by_multiple(seq, n):
for i in seq:
if i % n:
yield i
Usage:
>>> filter_by_multiple([2, 3, 4, 5, 6, 7, 8, 9, 10], 2)
<generator object filter_by_multiple at 0x000000374ED30258>
>>> list(filter_by_multiple([2, 3, 4, 5, 6, 7, 8, 9, 10], 2))
[3, 5, 7, 9]
>>>
Using a generator expression
While the above solution is fine, is can be shortened even more by using generator expressions. generator expression are like list comprehensions, but unlike them, they return a generator iterator instead of a list.
Usage:
>>> l = [2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(el for el in l if el % 2)
[3, 5, 7, 9]
>>>
Using filter():
Among many of the builtin functions in Python, there is one for filtering list called filter(). The usually way to use filter() is to pass in the function you want to use to filter your list, and then the actual list you want filtered. In your case, you want to filter out every element the is not a multiple of two:
Usage:
>>> l = [2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(filter(lambda x: x % 2, l))
[3, 5, 7, 9]
>>>
Using a list comprehension
While all of the above are fine ways for filtering a list, probably the most obvious and canonical, is to use a list comprehension. In your case, your list comprehension, is dead simple.
Usage:
>>> l = [2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> [el for el in l if el % 2]
[3, 5, 7, 9]
>>>

Python array segmented in ranges

I have the following array [1, 4, 7, 9, 2, 10, 5, 8] and I need to separate the array in 3 different arrays: one for values between 0 and 3, anther for 3 to 6 and anther for 6 and 25.The result must be something like that:
array1 = [1, 2]
array2 = [4, 5]
array3 = [7, 9, 10, 8]
Any idea about how to do it simple?
First, define your "pole" numbers
Second, generate your intervals from those "pole" numbers
Third, define as many lists as there are intervals.
Then for each interval, scan the list and appends items in the relevant list if they belong to the interval
code:
source = [1, 4, 7, 9, 2, 10, 5, 8]
poles = (0,3,6,25)
intervals = [(poles[i],poles[i+1]) for i in range(len(poles)-1)]
# will generate: intervals = [(0,3),(3,6),(6,25)]
output = [list() for _ in range(len(intervals))]
for out,(start,stop) in zip(output,intervals):
for s in source:
if start <= s <stop:
out.append(s)
print(output)
result:
[[1, 2], [4, 5], [7, 9, 10, 8]]
This solution has the advantage of being adaptable to more than 3 lists/intervals by adding more "pole" numbers.
EDIT: There's a nice & fast solution (O(log(N)*N)) if the output lists order don't matter:
first sort the input list
then generate the sliced sub-lists using bisect which returns insertion position of the provided numbers (left & right)
like this:
import bisect
source = sorted([1, 4, 7, 9, 2, 10, 5, 8])
poles = (0,3,6,25)
output = [source[bisect.bisect_left(source,poles[i]):bisect.bisect_right(source,poles[i+1])] for i in range(len(poles)-1)]
print(output)
result:
[[1, 2], [4, 5], [7, 8, 9, 10]]
You can do that in a very simple way using a combination of a for loop and range functions:
lists = ([], [], [])
for element in [1, 4, 7, 9, 2, 10, 5, 8]:
if element in range(0, 3):
lists[0].append(element)
elif element in range(3, 6):
lists[1].append(element)
elif element in range(6, 25):
lists[2].append(element)
array1, array2, array3 = lists
"One-line" solution using set.intersection(*others) and range(start, stop[, step]) functions:
l = [1, 4, 7, 9, 2, 10, 5, 8]
l1, l2, l3 = (list(set(l).intersection(range(3))), list(set(l).intersection(range(3,6))), list(set(l).intersection(range(6,25))))
print(l1)
print(l2)
print(l3)
The output:
[1, 2]
[4, 5]
[8, 9, 10, 7]
https://docs.python.org/3/library/stdtypes.html?highlight=intersection#set.intersection

Python combining items in a list of lists python

I have three items in a list of lists:
test = [[a,b,c],[d,e,f],[g,h,i]]
I want it to look like this:
test = [[a,b,c,d,e,f],[g,h,i]]
what is the best way to do this in python?
Thanks for the help
>>> test = [[1,2,3], [4,5,6], [7,8,9]]
>>> test[0].extend(test.pop(1)) # OR test[0] += test.pop(1)
>>> test
[[1, 2, 3, 4, 5, 6], [7, 8, 9]]
test = [test[0] + test[1], test[2]]
If you want to flatten of an arbitrary slice, use a slice assignment and a list comprehension on the part you want to flatten.
This flatten from position n to end of the list:
>>> test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
>>> n=2
>>> test[n:]=[[i for s in test[n:] for i in s]]
>>> test
[[1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15]]
This flattens up to n (but including n):
>>> test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
>>> test[0:n]=[[i for s in test[0:n] for i in s]]
>>> test
[[1, 2, 3, 4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]
This flattens in the middle (from and including n to include the additional groups specified):
>>> test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
>>> test[n:n+2]=[[i for s in test[n:n+2] for i in s]]
>>> test
[[1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15]]
Flatten all the sublists:
>>> test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
>>> n=len(test)
>>> test[0:n]=[[i for s in test[0:n] for i in s]]
>>> test
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
Note that in each case the slice on the assignment side is the same as the slice on the list comprehension side. This allows the use of the same slice object for each.
For the last case of flattening the whole list, obviously, you can just do test=[[i for s in test for i in s]] but the fact the logic is consistent allows you to wrap this in a function use a slice object.
You could combine the first two items in the list of list with the + operator and you should use '' for your strings
test = [['a','b','c'],['e','f','g'],['h','i','j']]
result = [test[0] + test[1], test[2]]
print result
output:
[['a', 'b', 'c', 'e', 'f', 'g'], ['h', 'i', 'j']]
Using the more_itertools package:
import more_itertools as mit
list(mit.chunked(mit.flatten(test), 6))
# [[1, 2, 3, 4, 5, 6], [7, 8, 9]]

Given a bunch of ranges of numbers, get all the numbers within those ranges?

In Python, you can get the numbers in a range by calling range(x,y). But given two ranges, say 5-15, and 10-20 how can you get all the numbers 5-20 without duplicates? The ranges may also be disjoint.
I could concat all the results and then uniquify the list, but is that the fastest solution?
>>> a = range(5, 15)
>>> b = range(10, 20)
>>> print sorted(set(a + b))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Or if you want a more general expansion of the lists to their elements for inclusion in the set:
>>> list_of_lists = [a, b]
>>> print sorted(set(elem for l in list_of_lists for elem in l))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
And I found a way to compose it all in one line:
>>> list_of_lists = [a, b]
>>> print set.union(*map(set, list_of_lists))
Is sorting necessary? It's just expository, but then I don't see that sets necessarily output in sorted order:
>>> x = set(range(3))
>>> x
set([0, 1, 2])
>>> x.add(-1)
>>> x
set([0, 1, 2, -1])
Or you can join overlapping ranges:
>>> def join_overlapping_ranges(ranges):
... list_of_ranges = []
... # ranges are sorted on first element
... for r in sorted(ranges):
... # ranges are stored as [start, end]
... if list_of_ranges and list_of_ranges[-1][1] >= r[0]:
... list_of_ranges[-1][1] = r[1]
... else:
... list_of_ranges.append(r)
... return list_of_ranges
...
>>> ranges = [[3,4], [5,7], [1,2], [4,6], [5,5]]
>>> print sorted(ranges)
[[1, 2], [3, 4], [4, 6], [5, 5], [5, 7]]
>>> print join_overlapping_ranges(ranges)
[[1, 2], [3, 7]]
Sort the ranges (x, y) by increasing x values. Now, for each range, if it overlaps the previous range, set your current "big range"'s y value to the current range's y value. If it doesn't, start a new "big range": this one won't overlap any of the previous ones. If the current range is completely included in the current big one, ignore it.
For the quantity you need I would just keep it simple
>>> a = range(5, 15)
>>> b = range(10, 20)
>>> from itertools import chain
>>> sorted(set(chain.from_iterable(list_of_lists)))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

Categories

Resources