Counting sequential occurrences in a list and - python

I have 3 lists as follows:
L1 = ['H', 'H', 'T', 'T', 'T', 'H', 'H', 'H', 'H', 'T']
L2 = ['H', 'H', 'T', 'T', 'T', 'H', 'H', 'H', 'H', 'T' , 'T', 'H, 'T', 'T', 'T', 'H', 'H', 'H', 'T']
L3 = ['H', 'T', 'H', 'H']
I would like to count sequential occurrences of 'H' in each list and produce the following table showing the frequencies of these 'H' sequences:
Length | L1 | L2 | L3
----------------------
1 0 1 1
2 1 1 1
3 0 1 0
4 1 1 0
5 0 0 0
I know that doing the following gives me the frequnecies of a sequence in a list:
from itertools import groupby
[len(list(g[1])) for g in groupby(L1) if g[0]=='H']
[2, 4]
But am in need of an elegant way to take this further over the remaining lists and ensuring that a '0' is placed for unobserved lengths.

You can use collections.Counter to create a frequency dict from a generator expression that outputs the lengths of sequences generated by itertools.groupby, and then iterate through a range of possible lengths to output the frequencies from the said dict, with 0 as a default value in absence of a frequency.
Using L1 as an example:
from itertools import groupby
from collections import Counter
counts = Counter(sum(1 for _ in g) for k, g in groupby(L1) if k == 'H')
print([counts[length] for length in range(1, 6)])
This outputs:
[0, 1, 0, 1, 0]

You can use itertools.groupby with collections.Counter:
import itertools as it, collections as _col
def scores(l):
return _col.Counter([len(list(b)) for a, b in it.groupby(l, key=lambda x:x == 'H') if a])
L1 = ['H', 'H', 'T', 'T', 'T', 'H', 'H', 'H', 'H', 'T']
L2 = ['H', 'H', 'T', 'T', 'T', 'H', 'H', 'H', 'H', 'T' , 'T', 'H', 'T', 'T', 'T', 'H', 'H', 'H', 'T']
L3 = ['H', 'T', 'H', 'H']
d = {'L1':scores(L1), 'L2':scores(L2), 'L3':scores(L3)}
r = '\n'.join([f'Length | {" | ".join(d.keys())} ', '-'*20]+[f'{i} {" ".join(str(b.get(i, 0)) for b in d.values())}' for i in range(1, 6)])
print(r)
Output:
Length | L1 | L2 | L3
--------------------
1 0 1 1
2 1 1 1
3 0 1 0
4 1 1 0
5 0 0 0

This might work :
from itertools import groupby
a = [len(list(v)) if k=='H' and v else 0 for k,v in groupby(''.join(L1))]
For a sample L4 = ['T', 'T'] where there is no 'H' item in list, it returns [0].
For L1 it returns [2, 0, 4, 0].
For L2 it returns [2, 0, 4, 0, 1, 0, 3, 0].
For L3 it returns [1, 0, 2].

Please try max([len(x) for x in ''.join(y).split('T')]) where y is your list.

Related

How to print an index 0 of a list, with for loop?

Why does this code, does not return (or print) the 0 index of the list?
N = int(input())
letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
for i in range(N):
print(letters[i]*i)
output: should be:
A
BB
CCC
DDDD
EEEEE
FFFFFF
GGGGGGG
But im getting this, without the "A":
B
CC
DDD
EEEE
FFFFF
GGGGGG
Because i starts counting at zero.
If you type:
for i in range(10):
print(i)
You'll see:
0
1
2
3
4
5
6
7
8
9
If you want to count from 1 to N instead of 0 to N-1, type:
print(letters[i]*(i+1))
Because 0 times a list wont print it. You need to add 1.
N = int(input())
letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
for i in range(N):
print(letters[i]*(i+1))
the for i in range starts at 0. It then tries to execute
print(letters[i]*i)
At index 0, this translates to
print(letters[0]*0)
Since the resulting string is multiplied by zero, it outputs nothing. Changing i to i + 1 would solve this issue, as other commenters have pointed out.
the i starts from 0.
So you need:
for i in range(N):
print(letters[i]*(i+1))

Python how to revert the pattern of a list rearrangement

So I am rearranging a list based on an index pattern and would like to find a way to calculate the pattern I need to revert the list back to its original order.
for my example I am using a list of 5 items as I can work out the pattern needed to revert the list back to its original state.
However this isn't so easy when dealing with 100's of list items.
def rearrange(pattern: list, L: list):
new_list = []
for i in pattern:
new_list.append(L[i-1])
return new_list
print(rearrange([2,5,1,3,4], ['q','t','g','x','r']))
#['t', 'r', 'q', 'g', 'x']
and in order to set it back to the original pattern
I would use
print(rearrange([3,1,4,5,2],['t', 'r', 'q', 'g', 'x']))
#['q', 't', 'g', 'x', 'r']
What I am looking for is a way to calculate the pattern "[3,1,4,5,2]"
regarding the above example.
whist running the script so that I can set the list back to its original order.
Using a larger example:
print(rearrange([18,20,10,11,13,1,9,12,16,6,15,5,3,7,17,2,19,8,14,4],['e','p','b','i','s','r','q','h','m','f','c','g','d','k','l','t','a','n','j','o']))
#['n', 'o', 'f', 'c', 'd', 'e', 'm', 'g', 't', 'r', 'l', 's', 'b', 'q', 'a', 'p', 'j', 'h', 'k', 'i']
but I need to know the pattern to use with this new list in order to return it to its original state.
print(rearrange([???],['n', 'o', 'f', 'c', 'd', 'e', 'm', 'g', 't', 'r', 'l', 's', 'b', 'q', 'a', 'p', 'j', 'h', 'k', 'i']))
#['e','p','b','i','s','r','q','h','m','f','c','g','d','k','l','t','a','n','j','o']
This is commonly called "argsort". But since you're using 1-based indexing, you're off-by-one. You can get it with numpy:
>>> pattern
[2, 5, 1, 3, 4]
>>> import numpy as np
>>> np.argsort(pattern) + 1
array([3, 1, 4, 5, 2])
Without numpy:
>>> [1 + i for i in sorted(range(len(pattern)), key=pattern.__getitem__)]
[3, 1, 4, 5, 2]
What about something like below:
def revert_pattern(pattern):
pattern_i = [0]*len(pattern)
for k in range(len(pattern)):
pattern_i[pattern[k]-1] = k+1
return pattern_i
print(revert_pattern([2, 5, 1, 3, 4]))
# [3, 1, 4, 5, 2]
Note: I followed your logic but I recommend you using 0 as the smallest indexes instead of 1 since it requires somes extra +1/-1 that could be avoided
def rearrange(p, l):
arr = [l[i - 1] for i in p]
d = {v : i + 1 for i, v in enumerate(arr)}
order = [d[k] for k in l]
return arr, order
a = [2, 5, 1, 3, 4]
b = ['q', 't', 'g', 'x', 'r']
rearrange(a, b)
# (['t', 'r', 'q', 'g', 'x'], [3, 1, 4, 5, 2])
OR maybe
def revert(p):
z = zip(p, list(range(len(p))))
return [x + 1 for _, x in sorted(z)]
a = [2, 5, 1, 3, 4]
revert(a)
# [3, 1, 4, 5, 2]

Find the index of a value/char without using any list methods

def find(my_list, value):
find = -1
for i in range(length(my_list)):
if my_list[i] == value:
find += 1
return find
def length(my_list):
list_length = 0
for char in my_list:
list_length += 1
return list_length
str_list1 = ['r', 'i', 'n', 'g', 'i', 'n', 'g']
print("\nfind Test")
print(list_function.find(str_list1, 'g'))
print(list_function.find(str_list1, 'z'))
The output is 0 and -1, while we are looking for 3 and -1.
Need to create it without using built-in methods.
Use enumerate:
def find(my_list, value):
for index, element in enumerate(my_list):
if element == value:
return index
return -1
print(find(['r', 'i', 'n', 'g', 'i', 'n', 'g'], 'g'))
# 3
print(find(['r', 'i', 'n', 'g', 'i', 'n', 'g'], 'z'))
# -1
If you need no built-ins to be used, this is one way:
def find(my_list, value):
index = 0
for element in my_list:
if element == value:
return index
index += 1
return -1
print(find(['r', 'i', 'n', 'g', 'i', 'n', 'g'], 'g'))
# 3
print(find(['r', 'i', 'n', 'g', 'i', 'n', 'g'], 'z'))
# -1

fPython : Making a new list from a random list of letters

letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']#alphabet
bag_o_letters = []#letters to chose from
letter_count = [9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1]#random indexes to chose from
for x in range(26):#loops through the random index of letter_count
for i in range(letter_count[x]):#chooses the index
bag_o_letters.append(letters[x])#appends the index of the letter to bag_o_letters
rack = []#list for the person to see
for a in range(7):#makes the list 7 letters long
rack.append(bag_o_letters.pop(random.randint(0,len(letters)-1)))#appends the letter to rack(supposedly...)
print(rack)
In this code that you just read it should choose random letters and put 7 of those letters in a rack that the person can see. It shows a error that I've looked over many times, but I just can't see what is wrong.
I put comments on the side to understand the code.
It shows this error:
rack.append(bag_of_letters.pop(random.randint(0,len(letters)-1)))
IndexError: pop index out of range
Can someone please help?
After this code, I am going to make a input statement for the user to make a word from those letters.
The first time through the loop, you append one value to bag_of_letters, and then you try to pop an index of random.randint(0,len(letters)-1). It doesn't have that many elements to pop from yet. Instead of this approach, you can make a list of the required length and sample from it:
letters = ['a', ...]#alphabet
letter_count = [9, ...]#random indexes to chose from
bag_of_letters = [l*c for l,c in zip(letters, letter_count)]
...
rack = random.sample(bag_o_letters, 7)
You're selecting the index to pop for bag_of_letters from the length of letters which is obviously larger.
You should instead do:
rack.append(bag_of_letters.pop(random.randint(0, len(bag_of_letters)-1)))
# ^^^^^^^^^^^^^^
However, there are likely to be more problems with your code. I'll suggest you use random.sample in one line of code or random.shuffle on a copy of the list, and then slice up till index 7. Both will give you 7 randomly selected letters:
import random
print(random.sample(letters, 7))
# ['m', 'u', 'l', 'z', 'r', 'd', 'x']
import random
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
letters_copy = letters[:]
random.shuffle(letters_copy)
print(letters_copy[:7])
# ['c', 'e', 'x', 'b', 'w', 'f', 'v']
The IndexError is expected:
pop(...)
L.pop([index]) -> item -- remove and return item at index (default last).
Raises IndexError if list is empty or index is out of range.
You need to subtract 1 from the bounds of the call to random() after each pop(). Right now you are doing this:
l = [1,2,3]
random_idx = 2
l.pop(random_idx)
>>> l == [1,3]
random_idx = 3
l.pop(random_idx)
>>>> IndexError: pop index out of range
So instead, pop() based on len(bag_o_letter) rather than len(letter).
Why not do something like this:
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
letter_count = [9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1]#random indexes to chose from
from random import shuffle
all_letters = list(''.join([l*c for l,c in zip(letters, letter_count)]))
shuffle(all_letters)
for i in range(int(len(all_letters)/7)):
print all_letters[i*7:(i+1)*7]
So I assume this is for something like scrabble? Your issue is that you're choosing a random index from your list of letters, not bag_o_letters. Maybe try this:
rack = []
for i in range(7):
index = random.randint(0, len(bag_o_letter) - 1)
rack.append(bag_o_letters.pop(index))

python, given 2 lists, take off the same middle part then added rest 2 lists

In python, need to define a function that takes 2 lists. If the last part of the first list is same as first part of the second list, then delete the same part and added rest 2 lists together. such as:
I have defined a helper function called prefix that takes 2 lists and return true if the first list is prefix of the second list. Otherwise return false.
You could try:
def merge(seq1, seq2):
n1 = len(seq1)
for i in range(n1):
if seq1[i:] == seq2[:n1-i]:
return seq1 + seq2[n1-i:]
return seq1 + seq2
In [52]: merge(['a','a','a'],['g','g','g'])
Out[52]: ['a', 'a', 'a', 'g', 'g', 'g']
In [53]: merge(['a','t','t','a'],['t','t','a','a'])
Out[53]: ['a', 't', 't', 'a', 'a']
In [54]: merge(['a','t','t','a'],['t','t','a','a'])
Out[54]: ['a', 't', 't', 'a', 'a']
In [55]: merge(['a', 'a', 'a', 't', 't', 't', 't'], ['t', 't', 't', 't', 'g', 'g', 'g', 'g'])
Out[55]: ['a', 'a', 'a', 't', 't', 't', 't', 'g', 'g', 'g', 'g']
It doesn't modify the original lists, but you could do this easily if you need to.
(This isn't the way to go if your lists are very long with short overlaps because it checks from the beginning of the first.)
def merge(L1, L2):
middle = []
while L1 and L2 and L1[-1] == L2[0]:
middle.append(L2[0])
L1 = L1[:-2]
L2 = L2[1:]
return L1 + middle + L2

Categories

Resources