Combining multiple for loops that iterate over matrix python - python

hello I want to combine multiple for loops into one loop to make my code less complex.
the loop iterates over the elements in a list of lists and add +1 for every correct value.
so let's say i have
ink = 0
mouse = 0
rat = 0
matrix = [
['', 'article1.txt', 'article2.txt', 'article3.txt'], ['ink', 2, 3, 0], ['mouse', 0, 2, 1], ['rat', 0, 0, 1]]
and
for line in matrix[1:2]:
for i in line[1:]:
if i > 0:
ink = ink + 1
for line in matrix[2:3]:
for i in line[1:]:
if i > 0:
mouse = mouse + 1
for line in matrix[3:4]:
for i in line[1:]:
if i > 0:
rat = rat + 1
I'd want this to become one loop or atleast some shorter code that automatically does this, even if there would be more rows and columns in the matrix.

So assuming a correct value is a value greater than 1, i did this: Also to have infinite values, i organized the results in a dictionary:
matrix = [
["", "article1.txt", "article2.txt", "article3.txt"],
["ink", 2, 3, 0],
["mouse", 0, 2, 1],
["rat", 0, 0, 1]]
results = {product[0]: [1 for a in product[1:] if a > 0].count(1) for product in matrix[1:]}
print(results)
I hope this is what you expected. Let me know if you need any adjustments or explanations. This would be the result for your example:
{'ink': 2, 'mouse': 2, 'rat': 1}

One way to improve your code, would be:
>>> d = {}
>>> for row in matrix[1:]:
d[row[0]] = len(list(filter(lambda x: x>0, row[1:])))
>>> d
{'ink': 2, 'mouse': 2, 'rat': 1}
This also could be compacted into one liner dictionary comprehension:
>>> d = {row[0]:len(list(filter(lambda x: x>0, row[1:]))) for row in matrix[1:]}

Related

Combinations with Replacement and maximal Occurrence Constraint

from itertools import *
import collections
for i in combinations_with_replacement(['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'],15):
b = (''.join(i))
freq = collections.Counter(b)
for k in freq:
if freq [k] < 5:
print(k)
this code most print chars what count if less than 5
what i try do , cheek if at string from join at fly if there is repeated any of characters les than x times at any possition of that string and print strings only what true to that.
Problem is no mater what i try do , or its print all and ignore if ... or print notting.
how do it right , or maybe at python exist simple solution ?
Result most be as example les than 5
False - fffaaffbbdd ( repeat 5 titemes f)
False - fffffaaaaac ( repeat 5 times a and f)
True - aaabbbccc11 ( no any character repeated more than 4 times )
More clear explain qustion - filter all string with characters more than x repetions before give to next function.
As examble - there is simple print that strings , and not print strings what not at rule.
If I understand you right, you want to print strings where each character is found only 4-times at maximum:
from collections import Counter
from itertools import combinations_with_replacement
for i in combinations_with_replacement(['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'],15):
c = Counter(i)
if c.most_common(1)[0][1] > 4:
continue
print(''.join(i))
Prints:
...
00002446899cccd
00002446899ccce
00002446899cccf
00002446899ccdd
...
a more constructive approach (meaning: i do not iterate over all possible combinations - i construct the valid combinations directly).
you need to have sympy installed for this to work.
in the example i only use the elements "abcdef" and restrict the repetitions to be strictly smaller than MAX = 4. i fix the length of the strings to be output at M = 6.
i start by getting all the partitions of M with restricted repetitions k=MAX - 1 and not constisting of more than m=N parts. i immediately convert those to a list:
{3: 2} [3, 3, 0, 0, 0, 0]
{3: 1, 2: 1, 1: 1} [3, 2, 1, 0, 0, 0]
{3: 1, 1: 3} [3, 1, 1, 1, 0, 0]
{2: 3} [2, 2, 2, 0, 0, 0]
{2: 2, 1: 2} [2, 2, 1, 1, 0, 0]
{2: 1, 1: 4} [2, 1, 1, 1, 1, 0]
{1: 6} [1, 1, 1, 1, 1, 1]
of those lists i iterate over the multiset permutations - i mean those to represent the elements that i select and how often they are repeated: e.g:
[2, 1, 2, 0, 0, 1] -> "aabccf" # 2*"a", 1*"b", ..., 0*"e", 1*"f"
the result you want is then the multiset permutation of those strings.
from sympy.utilities.iterables import multiset_permutations, partitions
MAX = 4 # (all counts < MAX)
elements = "abcdef"
N = len(elements)
M = 6 # output length
def dict_to_list(dct, N):
ret = [0] * N
j = 0
for k, v in dct.items():
ret[j:j + v] = [k] * v
j += v
return ret
for dct in partitions(M, k=MAX - 1, m=N):
lst = dict_to_list(dct, N)
for part in multiset_permutations(lst):
el = ''.join(n * v for n, v in zip(part, elements))
for msp in multiset_permutations(el):
print(''.join(msp))
for your case you'd then need to change:
MAX = 5 # (all counts < MAX)
elements = "0123456789abcdef"
M = 15 # output length
but the complexity of that is huge (but way better that the one of the original approach)!

Comparing lists of different sizes and return an indices

I have 2 lists ...
The first is something like
a = [imgType_a, imgType_b, imgType_c]
The second is something like
b = [img_foo_a, img_foo_b, img_foo_c, img_bar_a, img_bar_b, img_bar_c]
I'd like to compare the lists and return a list of indices that correspond a where the last letter in a matches the last letter in b
For the example about I'd like to return ...
[0, 1, 2, 0, 1, 2]
if
a = [imgType_c, imgType_b, imgType_a]
and
b = [img_foo_a, img_foo_b, img_foo_c, img_bar_c, img_bar_a, img_bar_b, img_baz_a, img_qux_a]
the result would be
[2, 1, 0, 0, 2, 1, 2, 2]
The length of the list b will always be >=a.
You can use index to find the index of each element in b where it matches a (once you've converted those to be just the last letter). This can be done in a list comprehension:
>>> [[i[-1] for i in a].index(x) for x in [i[-1] for i in b]]
[0, 1, 2, 0, 1, 2]
Or, more efficient and clearer, convert your arrays first:
>>> a_last = [i[-1] for i in a]
>>> b_last = [i[-1] for i in b]
>>> [a_last.index(x) for x in b_last]
[0, 1, 2, 0, 1, 2]
Instead of using a list, use a dictionary:
img_type_map = {img_type[-1]: i for i, img_type in enumerate(a)}
output = [img_type_map[img[-1]] for img in b]

Python: How to group elements in the list and get sum of groups

I want to group list digits by their value and calculate sum of each group. Basically, it looks this way:
input:
list = [0,1,1,0,0,0,1,0,1,1,1,1,0]
OUT (which i want to get):
newList = [0,2,0,1,0,4,0]
Any ideas how to acheive this in Python?
Thanks
You can use itertools.groupby:
import itertools
s = [0,1,1,0,0,0,1,0,1,1,1,1,0]
final_s = [sum(b) for _, b in itertools.groupby(s)]
Output:
[0, 2, 0, 1, 0, 4, 0]
Here is recursive approach with one loop.
list1 = [0,1,1,0,0,0,1,0,1,1,1,1,0]
final_=[]
def recursive(lst):
track = []
if not lst:
return 0
else:
for i,j in enumerate(lst):
try:
if lst[i]==lst[i+1]:
track.append((lst[i],lst[i+1]))
else:
track.append(lst[i])
final_.append(track)
return recursive(lst[i+1:])
except IndexError:
final_.append([i])
recursive(list1)
print(list(map(lambda x:0 if 0 in x else len(x),final_)))
output:
[0, 2, 0, 1, 0, 4, 0]
Try this:
ist = [0,1,1,0,0,0,1,0,1,1,1,1,0]
a=[[]]
[a[-1].append(e) if e==ist[c-1] else a.append([e]) for c,e in enumerate(ist)]
new_list=[sum(l) for l in a]
Doesn't need libraries. Output:
[0, 2, 0, 1, 0, 4, 0]

Sum of specific elements in a list, if they are consecutive (python)

What question is asking for is, from a list of lists like the following, to return a tuple that contains tuples of all occurrences of the number 2 for a given index on the list. If there are X consecutive 2s, then it should appear only one element in the inside tuple containing X, just like this:
[[1, 2, 2, 1],
[2, 1, 1, 2],
[1, 1, 2, 2]]
Gives
((1,), (1,), (1, 1),(2,))
While
[[2, 2, 2, 2],
[2, 1, 2, 2],
[2, 2, 1, 2]]
Gives
((3,),(1, 1),(2,)(3,))
What about the same thing but not for the columns, this time, for the rows? is there a "one-line" method to do it? I mean:
[[1, 2, 2, 1],
[2, 1, 1, 2],
[1, 1, 2, 2]]
Gives
((2,), (1, 1), (2,))
While
[[2, 2, 2, 2],
[2, 1, 2, 2],
[2, 2, 1, 2]]
Gives
((4,),(1, 2),(2, 1))
I have tried some things, this is one of the things, I can't finish it, don't know what to do anymore, after it:
l = [[2,2,2],[2,2,2],[2,2,2]]
t = (((1,1),(2,),(2,)),((2,),(2,),(1,1)))
if [x.count(0) for x in l] == [0 for x in l]:
espf = []*len(l)
espf2 = []
espf_atual = 0
contador = 0
for x in l:
for c in x:
celula = x[c]
if celula == 2:
espf_atual += 1
else:
if celula == 1:
espf[contador] = [espf_atual]
contador += 1
espf_atual = 0
espf2 += [espf_atual]
espf_atual = 0
print(tuple(espf2))
output
(3, 3, 3)
this output is the correct one but if I change the list(l) it doesn't work
So, you have som emistakes in the code.
Indexing:
for c in x:
celula = x[c]
It should be celula = c as c already points to each element of x.
Intermediate results
For each column you store intermediate results as:
espf_atual = 0
...
espf_atual += 1
...
espf2 += [espf_atual]
but this will only allow to store the last occurrences of 2 for each column. This is, if a row is [2,1,2,2], then espf_actual = 2 and you will store only the last occurrence. You will override the first occurrence (before the 1).
To avoid this, you need to store intermediate results for each row. You got it halfway with espf = []*len(l), but you never used it properly later.
Find bellow a working example (not too different from your initial solution):
espf = []
for x in l:
# Restart counters for every row
espf_current = [] # Will store any sequences of 2
contador = 0 # Will count consecutive 2's
for c in x:
celula = c
if celula == 2:
contador += 1 # Count number of 2
elif celula == 1:
if contador > 0: # Store any 2 before 1
espf_current += [contador]
contador = 0
if contador > 0: # Check if the row ends in 2
espf_current += [contador]
# Store results of this row in the final results
espf += [tuple(espf_current)]
print tuple(espf)
The key to switch rows and columns, is to change the indexing method. Currently you are iterating along the elements of the list, and thus, this doesn't allow you to switch between rows and columns.
Another way to see the iteration is to iterate indexes of the matrix (i, j for rows and columns) as follows:
numRows = len(l)
numCols = len(l[0])
for i in range(numRows):
for j in range(numCols):
celula = l[i][j]
The above is the indexing equivalent to the previous code. It assumes all the rows have the same length (which is true in your examples). Changing it from rows to columns is straightforward (tip: switch the loops), I leave it to you :P

Counting same elements in an array and create dictionary

This question might be too noob, but I was still not able to figure out how to do it properly.
I have a given array [0,0,0,0,0,0,1,1,2,1,0,0,0,0,1,0,1,2,1,0,2,3] (arbitrary elements from 0-5) and I want to have a counter for the occurence of zeros in a row.
1 times 6 zeros in a row
1 times 4 zeros in a row
2 times 1 zero in a row
=> (2,0,0,1,0,1)
So the dictionary consists out of n*0 values as the index and the counter as the value.
The final array consists of 500+ million values that are unsorted like the one above.
This should get you what you want:
import numpy as np
a = [0,0,0,0,0,0,1,1,2,1,0,0,0,0,1,0,1,2,1,0,2,3]
# Find indexes of all zeroes
index_zeroes = np.where(np.array(a) == 0)[0]
# Find discontinuities in indexes, denoting separated groups of zeroes
# Note: Adding True at the end because otherwise the last zero is ignored
index_zeroes_disc = np.where(np.hstack((np.diff(index_zeroes) != 1, True)))[0]
# Count the number of zeroes in each group
# Note: Adding 0 at the start so first group of zeroes is counted
count_zeroes = np.diff(np.hstack((0, index_zeroes_disc + 1)))
# Count the number of groups with the same number of zeroes
groups_of_n_zeroes = {}
for count in count_zeroes:
if groups_of_n_zeroes.has_key(count):
groups_of_n_zeroes[count] += 1
else:
groups_of_n_zeroes[count] = 1
groups_of_n_zeroes holds:
{1: 2, 4: 1, 6: 1}
Similar to #fgb's, but with a more numpythonic handling of the counting of the occurrences:
items = np.array([0,0,0,0,0,0,1,1,2,1,0,0,0,0,1,0,1,2,1,0,2,3])
group_end_idx = np.concatenate(([-1],
np.nonzero(np.diff(items == 0))[0],
[len(items)-1]))
group_len = np.diff(group_end_idx)
zero_lens = group_len[::2] if items[0] == 0 else group_len[1::2]
counts = np.bincount(zero_lens)
>>> counts[1:]
array([2, 0, 0, 1, 0, 1], dtype=int64)
This seems awfully complicated, but I can't seem to find anything better:
>>> l = [0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 0, 0, 0, 1, 0, 1, 2, 1, 0, 2, 3]
>>> import itertools
>>> seq = [len(list(j)) for i, j in itertools.groupby(l) if i == 0]
>>> seq
[6, 4, 1, 1]
>>> import collections
>>> counter = collections.Counter(seq)
>>> [counter.get(i, 0) for i in xrange(1, max(counter) + 1)]
[2, 0, 0, 1, 0, 1]

Categories