How to group elements on tape of data?

Viewed 71

I have got sequence of stings like:

"aa", "lot", "bb", "obj", "obj", "obj", "cc", "lot", "obj", "gg", "lot", "obj", "obj"

I need to calculate how much every lot have obj. Result should be like:

[lot 3 obj] [lot 1 obj] [lot 2 obj]

NOT simply:

[3, 1, 2]

Or sometinkg like this.

String have some crap - any other symbols except lot and obj. Delemeter is starting new lot section.

5 Answers

Given your input:

inp = ["aa", "lot", "bb", "obj", "obj", "obj", "cc", "lot", "obj", "gg", "lot", "obj", "obj"]

Two methods:

  • a nicely readable generator function:

def group_lots(inp):
    count = 0
    seen_lot = False
    for item in inp:
        if item == "obj":
            count += 1
        if item == "lot":
            if seen_lot:
                yield count
            count = 0
            seen_lot = True
    if count:
        yield count

print(list(group_lots(inp)))  # [3, 1, 2]

  • or an unreadable mystical magical itertools.groupby expression:
import itertools

obj_counts = [
    len(list(group_contents))
    for is_lot, group_contents in itertools.groupby(
        (item for item in inp if item in ("lot", "obj")),
        lambda i: i == "lot",
    )
    if not is_lot
]
print(obj_counts)  # [3, 1, 2]

You could use enumerate to get the lot position, then count the obj in the sublists:

lst = ["aa", "lot", "bb", "obj", "obj", "obj", "cc", "lot", "obj", "gg", "lot", "obj", "obj"]
lot = [i for i, x in enumerate(lst) if x == "lot"]
obj = [lst[a:b].count("obj") for a, b in zip(lot, lot[1:] + [len(lst)])]
print(obj) # [3, 1, 2]

Or first remove the "crap" from the list, then you don't need the sublists and count later:

lst = [x for x in lst if x in ("lot", "obj")]
lot = [i for i, x in enumerate(lst) if x == "lot"]
obj = [b - a - 1 for a, b in zip(lot, lot[1:] + [len(lst)])]

(Both will not count any obj before the first lot, but after the last.)

One way using OrderedDict:

from collections import OrderedDict

d = OrderedDict()
for n, i in enumerate(l):
    if i == "lot":
        d[n] = [i]
    elif i == "obj":
        d[max(d)].append(i)
list(d.values())

Output:

[['lot', 'obj', 'obj', 'obj'], ['lot', 'obj'], ['lot', 'obj', 'obj']]
l="aa", "lot", "bb", "obj", "obj", "obj", "cc", "lot", "obj", "gg", "lot", "obj", "obj"
i=0
x=[]
while i<len(l):
    if l[i]=="lot":
      x.append(i)
    i+=1
j=0
s=[]
while j<len(x):
    s.append(l[x[j]:len(l) if j==len(x)-1 else x[j+1]].count("obj"))
    j+=1
    
print(s)

Try with numpy:

import numpy as np

lst=["aa", "lot", "bb", "obj", "obj", "obj", "cc", "lot", "obj", "gg", "lot", "obj", "obj"]

lst=np.array(lst)

res=np.cumsum(lst[np.isin(lst, ["lot", "obj"])]=="lot")

_, res=np.unique(res, return_counts=True)

# to exclude 'lot' from the count:
res-=1

Outputs:

>>> res

[3 1 2]
Related