Related
I've been using Python for a long time, but I've recently discovered a new way of creating and iterating through lists:
l = [x for x in range(0, 10)]
This produces a list from 0 to 9. It's very useful, and much simpler than what I had been doing before:
l = []
for x in range(0, 9):
l.append(x)
Now that I understand this way of doing things, I've been using it for a lot of stuff, and it's working out great. The only problem is, I sometimes want another option. For example, if I have a list full of 1's and 0's:
import random
l = [random.randint(0, 1) for i in range(0, 10)]
I want to take this list and iterate through again, only do two different things based on an if else statement:
for idx, item in enumerate(l):
if item == 1:
l[idx] = 'A'
else:
l[idx] = 'B'
I know it would be easier to just create a list full of random instances of 'A' and 'B', but this is just an example, it won't work in my use case. How would I use my new way of creating lists to do this? I know how to add an if statement on the end:
l = [x for x in range(0, 10)]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l = [x for x in range(0, 10) if i != 7]
# [0, 1, 2, 3, 4, 5, 6, 8, 9]
But what about my else statement? And, incidentally, what is this way of creating lists called? I might have been able to find an answer online if I knew.
What you want to do is called list comprehension and it is very helpful indeed!
This post answers your question: if/else in a list comprehension
And in your example, you want to do:
L = ['A' if random.randint(0, 1)==1 else 'B' for i in range(0, 10)]
Ok so let's clear some things up. Firstly, this way of creating lists is called list comprehension.
Now, let's talk about how to achieve a list of A's and B's(by list comprehension):
import random
# This creates a list of 1's and 0's
l = [random.randint(0, 1) for i in range(0, 10)]
# [1, 1, 1, 0, 0, 1, 1, 1, 1, 0]
# This creates a list of A's and B's
l = ['A' if num == 1 else 'B' for num in l]
#['A', 'A', 'A', 'B', 'B', 'A', 'A', 'A', 'A', 'B']
How to do it normally (without list comprehension)?
import random
l = []
for x in range(0, 10): # This should be 10 instead of 9 to get 10 values.
l.append(random.randint(0,1))
# l = [0, 1, 0, 1, 1, 0, 0, 0, 0, 1]
for idx, item in enumerate(l): # You need to use `enumerate()` to get index, value
if item == 1:
l[idx] = 'A'
else:
l[idx] = 'B'
# l = ['B', 'A', 'B', 'A', 'A', 'B', 'B', 'B', 'B', 'A']
You could make use of Python's ternary operator:
l = ['A' if i == 1 else 'B' for i in l]
The method of creating lists that you used is called a "list generator". Similarly, there is a dictionary generator.
To solve your problem, you need to add conditions inside the list generator:
import random
new_list = [random.randint(0, 1) for i in range(0, 10)]
another_new_list = ['A' if number == 1 else 'B' for number in new_list]
I want to create a recursive algorithm that will generate all permutations
of a specified length of a list of integers with some length n.
Following is my idea:
For every element in the list, I can remove it, then ask my recursive function to return to me all permutations of length k-1, and then to each of those permutations I will add the removed number. Then repeat this process for all numbers in the list.
The base cases are when the list is empty or contains only one element.
In these cases I just return the list. That is, as long as k is less than or equal to the length of the list (ex. if k is 3, but l = [1,2], I can't produce any permutations of length k).
This is what I've written:
def permutations(l, k):
w = len(l)
if (k <= w): # list is bigger than the length each permutations
if (w <= 1):
return list(l)
else:
result = []
for element in l:
listSmaller = l[:element] + l[element+1:]
for perm in permutations(listSmaller, k-1):
result.append([perm] + element)
return result
else: # list is not bigger than the length of the permutations, impossible.
print("k cannot exceed length of list")
I keep getting TypeError: can only concatenate list (not "int") to list
How should I modify this?
# code takes list lst and int n denoting length of permutation
# returns all permutations of length n over items in lst
def Perm(lst,n):
# if length of permutation is negative or 0, return empty
if n<=0:
return [[]]
# else take empty list l
l=[]
# loop over whole lst
for i in range(0,len(lst)):
m=lst[i] # current element of lst (ith)
remLst=lst[:i] + lst[i+1:] # remaining list i.e. all elements except ith
# recursive call to Perm with remaining list and decremented length
for p in Perm(remLst,n-1):
# append current element + all sublists p generated through recursion as an item in list l
l.append([m]+p)
return l
# some examples
# all permutations of length 2 over characters A,B,C
print(Perm(['A','B','C'],2))
# output: [['A', 'B'], ['A', 'C'], ['B', 'A'], ['B', 'C'], ['C', 'A'], ['C', 'B']]
# all permutations of length 2 over characters 1,2,3,4
print(Perm([1,2,3,4],2))
# output: [[1, 2], [1, 3], [1, 4], [2, 1], [2, 3], [2, 4], [3, 1], [3, 2], [3, 4], [4, 1], [4, 2], [4, 3]]
There are two problems:
First: [perm] + element Here you are adding a list to an integer.
Second: listSmaller = l[:element] + l[element+1:] Here you need an index to access the elements of the list. You are currently using the elements as an index and will therefore get an IndexError because when element=4, element+1 will be 5 but you do not have l[4+1:].
Your code works when I do the following changes in your code. I am only showing the modified lines. I am not sure if the output is as expected. You can try it and let me know.
for i, element in enumerate(l):
listSmaller = l[:i] + l[i+1:]
for perm in permutations(listSmaller, k-1):
result.append([perm] + [element])
In python, when using
for a in b:
'a' is not actually a number which can be used as an index, but is instead a pointer to an actual element in list 'b'. Put another way, if I had the following list
b = ["Bob", "Jim", "Jane"]
Then on the first iteration 'a' would be equal to "Bob", and not 0.
When you want to generate index numbers instead of pointers to elements, you can use:
for a in range(0, len(b)):
instead. Using this, your code should then work.
e.g.
for element in range(0, len(l)):
listSmaller = l[:element] + l[element+1:]
for perm in permutations(listSmaller, k-1):
result.append([perm] + element)
return result
I am trying to create a function that receives a list and return another list with the repeated elements.
For example for the input A = [2,2,1,1,3,2] (the list is not sorted) and the function would return result = [[1,1], [2,2,2]]. The result doesn't need to be sorted.
I already did it in Wolfram Mathematica but now I have to translate it to python3, Mathematica has some functions like Select, Map and Split that makes it very simple without using long loops with a lot of instructions.
result = [[x] * A.count(x) for x in set(A) if A.count(x) > 1]
Simple approach:
def grpBySameConsecutiveItem(l):
rv= []
last = None
for elem in l:
if last == None:
last = [elem]
continue
if elem == last[0]:
last.append(elem)
continue
if len(last) > 1:
rv.append(last)
last = [elem]
return rv
print grpBySameConsecutiveItem([1,2,1,1,1,2,2,3,4,4,4,4,5,4])
Output:
[[1, 1, 1], [2, 2], [4, 4, 4, 4]]
You can sort your output afterwards if you want to have it sorted or sort your inputlist , then you wouldnt get consecutive identical numbers any longer though.
See this https://stackoverflow.com/a/4174955/7505395 for how to sort lists of lists depending on an index (just use 0) as all your inner lists are identical.
You could also use itertools - it hast things like TakeWhile - that looks much smarter if used
This will ignore consecutive ones, and just collect them all:
def grpByValue(lis):
d = {}
for key in lis:
if key in d:
d[key] += 1
else:
d[key] = 1
print(d)
rv = []
for k in d:
if (d[k]<2):
continue
rv.append([])
for n in range(0,d[k]):
rv[-1].append(k)
return rv
data = [1,2,1,1,1,2,2,3,4,4,4,4,5,4]
print grpByValue(data)
Output:
[[1, 1, 1, 1], [2, 2, 2], [4, 4, 4, 4, 4]]
You could do this with a list comprehension:
A = [1,1,1,2,2,3,3,3]
B = []
[B.append([n]*A.count(n)) for n in A if B.count([n]*A.count(n)) == 0]
outputs [[1,1,1],[2,2],[3,3,3]]
Or more pythonically:
A = [1,2,2,3,4,1,1,2,2,2,3,3,4,4,4]
B = []
for n in A:
if B.count([n]*A.count(n)) == 0:
B.append([n]*A.count(n))
outputs [[1,1,1],[2,2,2,2,2],[3,3,3],[4,4,4,4]]
Works with sorted or unsorted list, if you need to sort the list before hand you can do for n in sorted(A)
This is a job for Counter(). Iterating over each element, x, and checking A.count(x) has a O(N^2) complexity. Counter() will count how many times each element exists in your iterable in one pass and then you can generate your result by iterating over that dictionary.
>>> from collections import Counter
>>> A = [2,2,1,1,3,2]
>>> counts = Counter(A)
>>> result = [[key] * value for key, value in counts.items() if value > 1]
>>> result
[[2, 2, 2], [[1, 1]]
For a one dimensional list, the index of an item is found as follows:
a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
a_list.index('mpilgrim')
What is the equivalent for a 2 or n dimensional list?
Edit: I have added an example to clarify:
If I have a 3 dimensional list as follows
b_list = [
[1,2],
[3,4],
[5,6],
[7,8]
],
[
[5,2],
[3,7],
[6,6],
[7,9]
]
Now lets say I want to identify a certain value in this list. If I know the index of the 1st and 2nd dimesion but don't know the zero-th index for the value I want, how do I go about finding the zero-th index?
Would it be something like:
target_value = 7
b_list[0].index(target_value)
With the output being an integer:
0
I don't know of an automatic way to do it, but if
a = [[1,2],[3,4],[5,6]]
and you want to find the location of 3, you can do:
x = [x for x in a if 3 in x][0]
print 'The index is (%d,%d)'%(a.index(x),x.index(3))
The output is:
The index is (1,0)
For two dimensional list; you can iterate over rows and using .index function for looking for item:
def find(l, elem):
for row, i in enumerate(l):
try:
column = i.index(elem)
except ValueError:
continue
return row, column
return -1
tl = [[1,2,3],[4,5,6],[7,8,9]]
print(find(tl, 6)) # (1,2)
print(find(tl, 1)) # (0,0)
print(find(tl, 9)) # (2,2)
print(find(tl, 12)) # -1
A multidimensional list is simply a list with more lists inside of it.
So its indices would be lists themselves.
a = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
print a.index([2, 3, 4])
# prints 1
list_2d = [[1,2],[3,4],[5,6]]
element = 1
index_row = [list_2d.index(row) for row in list_2d if element in row]
index_column = [row.index(element) for row in list_2d if element in row]
For multidimensional arrays:
def find(needle,haystack):
if needle == haystack: return []
# Strings are iterable, too
if isinstance(haystack,str) and len(haystack)<=1: return None
try:
for i,e in enumerate(haystack):
r = find(needle,e)
if r is not None:
r.insert(0,i)
return r
except TypeError:
pass
return None
ml = [[1,2,3],[4,5,6],[7,8,9]]
print find(2,ml)
ml = [3,[[1,2,3],[4,5,6],[7,8,9]]]
print find(2,ml)
ml = [[["ab", "bc", "cde"]]]
print find("d",ml)
There should be a better way to avoid the try/except block, but I could not find one:
In Python, how do I determine if an object is iterable?
You can use the following sample method too:
data = [[1, 1,2],[12,4],[6]]
def m_array_index(arr, searchItem):
for i,x in enumerate(a):
for j,y in enumerate(x):
if y == searchItem:
return i,j
return -1,-1#not found
print m_array_index(data, 6)
Or with all occurrences(sure code could be optimized - modified to work with generators and so on - but here is just a sample):
occurrences = lambda arr, val: tuple((i,j) for i,x in enumerate(arr) for j,y in enumerate(x) if y == val) or ((-1,-1))
print occurrences(data, 1) # ((0, 0), (0, 1))
print occurrences(data, 12) # ((1, 0),)
print occurrences(data, 11) # (-1, -1)
For n-dimensional recursive search, you can try something like this:
from copy import copy
def scope(word, list, indexes = None):
result = []
if not indexes:
indexes = []
for index, item in enumerate(list):
try:
current_index = indexes + [index]
result.append(current_index + [item.index(word)])
except ValueError:
pass
if type(item[0]) == type([]):
indexes.append(index)
result.extend(scope(word, item, copy(indexes)))
return result
And the result is:
>>> d_list = [['a', 'b', 'new', 'mpilgrim', 'new'], [['a', 'b', 'new', 'mpilgrim', 'new'], ['b', 'd', 'new', 'mpilgrim', 'new']]]
>>> word = 'mpilgrim'
>>> result = scope(word, d_list)
[[0, 3], [1, 0, 3], [1, 1, 3]]
Probably there are better ways to do it, but that is the one I figured out without getting any library.
EDIT:
Actually, it was not perfect and one library must be added. It's copy. Now it's ok.
If you want to find the list that has an item, the simplest way to do it is:
i = 4
index = b_list[0].index( filter(lambda 1D_list: i in index , b_list[0]) )
Or if you know there are more than one matches for the item, then you can do:
i = 4
indexes = []
for match in filter(lambda 1D_list: i in list, b_list[0]):
indexes.append(b_list[0].index(match))
None of this will raise any errors but they'll only work if there is no subarray. Go here for information about the functionality of filter.
list1 = [10, 20, [300, 400, [5000, 6000, [1, 6000, 2]], 6000, 500], 30, 40]
print(f'Your list is {list1}')
print('---------------------------------------------------------')
index = []
index_result=['6000 index is list1']
def try_this(list1, index):
for x, element in enumerate(list1):
if element == 6000:
index.append(x)
index_result.append(index[:])
index.pop()
elif type(element) == list:
index.append(x)
try_this(element, index)
index.pop()
print(try_this(list1, index))
print(index_result)
I want to get the number of times x appears in the nested list.
if the list is:
list = [1, 2, 1, 1, 4]
list.count(1)
>>3
This is OK. But if the list is:
list = [[1, 2, 3],[1, 1, 1]]
How can I get the number of times 1 appears? In this case, 4.
>>> L = [[1, 2, 3], [1, 1, 1]]
>>> sum(x.count(1) for x in L)
4
itertools and collections modules got just the stuff you need (flatten the nested lists with itertools.chain and count with collections.Counter
import itertools, collections
data = [[1,2,3],[1,1,1]]
counter = collections.Counter(itertools.chain(*data))
print counter[1]
Use a recursive flatten function instead of itertools.chain to flatten nested lists of arbitrarily level depth
import operator, collections
def flatten(lst):
return reduce(operator.iadd, (flatten(i) if isinstance(i, collections.Sequence) else [i] for i in lst))
reduce with operator.iadd has been used instead of sum so that the flattened is built only once and updated in-place
Here is yet another approach to flatten a nested sequence. Once the sequence is flattened it is an easy check to find count of items.
def flatten(seq, container=None):
if container is None:
container = []
for s in seq:
try:
iter(s) # check if it's iterable
except TypeError:
container.append(s)
else:
flatten(s, container)
return container
c = flatten([(1,2),(3,4),(5,[6,7,['a','b']]),['c','d',('e',['f','g','h'])]])
print(c)
print(c.count('g'))
d = flatten([[[1,(1,),((1,(1,))), [1,[1,[1,[1]]]], 1, [1, [1, (1,)]]]]])
print(d)
print(d.count(1))
The above code prints:
[1, 2, 3, 4, 5, 6, 7, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
1
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
12
Try this:
reduce(lambda x,y: x+y,list,[]).count(1)
Basically, you start with an empty list [] and add each element of the list list to it. In this case the elements are lists themselves and you get a flattened list.
PS: Just got downvoted for a similar answer in another question!
PPS: Just got downvoted for this solution as well!
If there is only one level of nesting flattening can be done with this list comprenension:
>>> L = [[1,2,3],[1,1,1]]
>>> [ item for sublist in L for item in sublist ].count(1)
4
>>>
For the heck of it: count to any arbitrary nesting depth, handling tuples, lists and arguments:
hits = lambda num, *n: ((1 if e == num else 0)
for a in n
for e in (hits(num, *a) if isinstance(a, (tuple, list)) else (a,)))
lst = [[[1,(1,),((1,(1,))), [1,[1,[1,[1]]]], 1, [1, [1, (1,)]]]]]
print sum(hits(1, lst, 1, 1, 1))
15
def nested_count(lst, x):
return lst.count(x) + sum(
nested_count(l,x) for l in lst if isinstance(l,list))
This function returns the number of occurrences, plus the recursive nested count in all contained sub-lists.
>>> data = [[1,2,3],[1,1,[1,1]]]
>>> print nested_count(data, 1)
5
The following function will flatten lists of lists of any depth(a) by adding non-lists to the resultant output list, and recursively processing lists:
def flatten(listOrItem, result = None):
if result is None: result = [] # Ensure initial result empty.
if type(listOrItem) != type([]): # Handle non-list by appending.
result.append(listOrItem)
else:
for item in listOrItem: # Recursively handle each item in a list.
flatten(item, result)
return result # Return flattened container.
mylist = flatten([[1,2],[3,'a'],[5,[6,7,[8,9]]],[10,'a',[11,[12,13,14]]]])
print(f'Flat list is {mylist}, count of "a" is {mylist.count("a")}')
print(flatten(7))
Once you have a flattened list, it's a simple matter to use count on it.
The output of that code is:
Flat list is [1, 2, 3, 'a', 5, 6, 7, 8, 9, 10, 'a', 11, 12, 13, 14], count of "a" is 2
[7]
Note the behaviour if you don't pass an actual list, it assumes you want a list regardless, one containing just the single item.
If you don't want to construct a flattened list, you can just use a similar method to get the count of any item in the list of lists, with something like:
def deepCount(listOrItem, searchFor):
if type(listOrItem) != type([]): # Non-list, one only if equal.
return 1 if listOrItem == searchFor else 0
subCount = 0 # List, recursively collect each count.
for item in listOrItem:
subCount += deepCount(item, searchFor)
return subCount
deepList = [[1,2],[3,'a'],[5,[6,7,[8,9]]],[10,'a',[11,[12,13,14]]]]
print(f'Count of "a" is {deepCount(deepList, "a")}')
print(f'Count of 13 is {deepCount(deepList, 13)}')
print(f'Count of 99 is {deepCount(deepList, 99)}')
As expected, the output of this is:
Count of "a" is 2
Count of 13 is 1
Count of 99 is 0
(a) Up to the limits imposed by Python itself of course, limits you can increase by just adding this to the top of your code:
import sys
sys.setrecursionlimit(1001) # I believe default is 1000.
I mention that just in case you have some spectacularly deeply nested structures but you shouldn't really need it. If you're nesting that deeply then you're probably doing something wrong :-)