pandas add rows based on column value

Viewed 1470

Assuming I have this dataframe:

    X     Y
0   a     1
1   b    10
2   c    11
3   d   100
4   e   101
5   f   110
6   g   111

I would like to decompose the column Y into rows so that each number with more than one digit 1 is broken into another number with only one digit 1. For example, the number 111, is broken to 3 rows with values 100, 10, and 1, and keeps the information from other columns. Here is a visualization of what I expect:

    X     Y
0   a     1
1   b    10
2   c    10
3   c     1
4   d   100
5   e   100
6   e     1
7   f   100
8   f    10
9   g   100
10  g    10
11  g     1

Here is what I have done so far, but I wonder if there is a more pythonic way to do it. Appreciate your help in advance.

import pandas as pd

df = pd.DataFrame({ 'X':['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o'], 'Y':[1,10,11,100,101,110,111,1000,1001,1010,1011,1100,1101,1110,1111] })
print(df)

for i in range(len(df)):
    value  = int(df.at[i,'Y'])
    digits = len(str(value))
    opts   = sum(map(int, str(value)))
    
    if opts > 1:
        # assign first value
        temp = df.loc[[i]]
        temp.at[i,'Y'] = 10**(digits-1)
        
        # update row
        df   = df.drop([i])
        df   = df.append(temp)

        # append new rows
        while value != 1:
            value  = int(str(value)[1:])
            if value == 0: break
            digits = len(str(value))
            temp.at[i,'Y'] = 10**(digits-1)
            df     = df.append(temp)
            
df = df.sort_index()
df = df.reset_index(drop=True)
print(df)
1 Answers

Try applying a decompose function like this and then simply explode.

The decompose function takes 111 and returns [100,10,1] or takes 110 and returns [100,10]. It does it by enumerating over each of the digits as a string in reverse and multiplying it to 10^i. Then returning it as a list without any 0s.

decompose = lambda x: [10**i for i,j in enumerate(str(x)[::-1]) if int(j)!=0]

df['Y'] = df['Y'].apply(decompose)
out = df.explode('Y')
print(out)
   X    Y
0  a    1
1  b   10
2  c    1
2  c   10
3  d  100
4  e    1
4  e  100
5  f   10
5  f  100
6  g    1
6  g   10
6  g  100

EDIT: This is only for the specific condition mentioned by OP where The number can only be made of zeros and ones. In general cases, please use the lambda function - Courtsey @Joe Ferndz

lambda x: [10**i for i,a in enumerate(str(x)[::-1]) for _ in range(int(a))]
Related