Question: I have pandas dataframe with this input:  data = { 'sec_id': ['1', '', '1.2', '1.3', '1.3.1', '1.3.2', '2', '2.1', '2.2', '2.3', '', '2.3.2', '2.3.3', '3', '4', '4.1', '4.1.1', '4.2', '4.3', '4.4', '5', '5.1', '5.2', '5.3', '5.3.1', '5.3.2', '5.3.3', '5.3.4', '5.3.5', '5.4', '5.5', '6', '6.1', '6.1.1', '6.2', '6.3', '6.4', '6.5', '6.6', '6.6.1', '6.6.2', '6.6.3', '6.7', '6.8', '6.9', '6.9.1', '', '', '6.9.2'], 'p_type': ['Heading1', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading1', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading3', 'Heading1', 'Heading1', 'Heading2', 'Heading3', 'Heading2', 'Heading2', 'Heading2', 'Heading1', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading3', 'Heading3', 'Heading3', 'Heading2', 'Heading2', 'Heading1', 'Heading2', 'Heading3', 'Heading2', 'Heading2', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading3', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading4', 'Heading4', 'Heading3'] } df = pd.DataFrame(data)  The problem is to populate the blank "document_section_id" values with accurate section ID values, using the preceding ones as references. Conditions:  The number of digits is determined by the "paragraph type" column. For example, for "Heading3," there should be 4 digits and 3 dots, like so: 1.2.3.1.  For each empty value, it should reference the preceding available "paragraph type" and increment by 1 accordingly. Example 1:Given the input, the section ID for the 12th row can be derived from the previous one, resulting in the computed value of 2.3.1.Example 2: For the 48th and 49th rows, the section ID needs to be derived as 6.9.1.1 and 6.9.1.2, respectively.   There can be max 10 levels of subsection, so that should be taken care irrespective of number of sub sections. Output:  sec_id = [ '1', '1.1', '1.2', '1.3', '1.3.1', '1.3.2', '2', '2.1', '2.2', '2.3', '2.3.1', '2.3.2', '2.3.3', '3', '4', '4.1', '4.1.1', '4.2', '4.3', '4.4', '5', '5.1', '5.2', '5.3', '5.3.1', '5.3.2', '5.3.3', '5.3.4', '5.3.5', '5.4', '5.5', '6', '6.1', '6.1.1', '6.2', '6.3', '6.4', '6.5', '6.6', '6.6.1', '6.6.2', '6.6.3', '6.7', '6.8', '6.9', '6.9.1', '6.9.1.1', '6.9.1.2', '6.9.2' ] p_type = [ 'Heading1', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading1', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading3', 'Heading1', 'Heading1', 'Heading2', 'Heading3', 'Heading2', 'Heading2', 'Heading2', 'Heading1', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading3', 'Heading3', 'Heading3', 'Heading2', 'Heading2', 'Heading1', 'Heading2', 'Heading3', 'Heading2', 'Heading2', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading3', 'Heading3', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading4', 'Heading4', 'Heading3' ]  This is what I tried but it's not giving accurate output: current_section_id = "" current_level = 0 for index, row in df.iterrows(): if row['sec_id'] == '': current_level += 1 section_id = current_section_id.split('.') section_id[current_level - 1] = str(int(section_id[current_level - 1]) + 1) section_id = '.'.join(section_id[:current_level]) current_section_id = section_id df.at[index, 'document_section_id'] = section_id else: current_section_id = row['sec_id'] current_level = row['paragraph_type'].count('Heading') - 1 

Answer: I don't think you need to do this in Pandas. You can create a class that tracks the current section and bumps it by 1, or appends a subsection, or truncates and increments, based on the paragraph type. Here is an example: class SectionCreator: def __init__(self): self.section = [0] def __call__(self, paragraph_type: str): depth = int(paragraph_type.replace('Heading', '')) section = self.section[:depth] if depth == len(section) + 1: section.append(1) elif depth > len(section) + 1: raise ValueError(f'Heading depth increased from {len(section)} to {depth}') else: section[depth - 1] += 1 self.section = section return '.'.join(map(str, self.section))  To use it, instantiate the object and pass in the paragraph types one at at time. headings = ['Heading1', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading1', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading3', 'Heading1', 'Heading1', 'Heading2', 'Heading3', 'Heading2', 'Heading2', 'Heading2', 'Heading1', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading3', 'Heading3', 'Heading3', 'Heading2', 'Heading2', 'Heading1', 'Heading2', 'Heading3', 'Heading2', 'Heading2', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading3', 'Heading3', 'Heading2', 'Heading2', 'Heading2', 'Heading3', 'Heading4', 'Heading4', 'Heading3'] sc = SectionCreator() for h in headings: print(sc(h))  The printed output is: 1 1.1 1.2 1.3 1.3.1 1.3.2 2 2.1 2.2 2.3 2.3.1 2.3.2 2.3.3 3 4 4.1 4.1.1 4.2 4.3 4.4 5 5.1 5.2 5.3 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.4 5.5 6 6.1 6.1.1 6.2 6.3 6.4 6.5 6.6 6.6.1 6.6.2 6.6.3 6.7 6.8 6.9 6.9.1 6.9.1.1 6.9.1.2 6.9.2  I added a exception for the case where a paragraph type jumps up by more than 1 level of depth. So going from a 2 to a 4 will raise an exception. sc = SectionCreator() sc('Heading1') # -> "1" sc('Heading2') # -> "1.1" sc('Heading4') # raises: ValueError: Heading depth increased from 2 to 4