Home > front end >  Python pandas shift null in certain columns Only
Python pandas shift null in certain columns Only

Time:06-08

I have a dict like this:

         A     B       C      D        E      F     G     H      I      J
0      A.1   Data             Data                   223   52
1      A.2   Data     Data    Data                   12    6
2            A.4      Data                           32    365
3                    A.5     Data                   100   88
4      A.6   Data                                   654   98
5                    A.7     Data                   356   56

And my desired output like this:

         A     B       C      D        E      F     G     H      I      J
0      A.1   Data     Data                          223   52
1      A.2   Data     Data    Data                  12    6
2      A.4   Data                                   32    365
3      A.5   Data                                   100   88
4      A.6   Data                                   654   98
5      A.7   Data                                   356   56

Only column A to column E will shift null, I have a current script using lamba but all dataframe shift the null values to the last column. I need certain columns only, any one can help me? THank you!

def shift_null(arr):
    return [x for x in arr if x == x]   [np.nan for x in arr if x != x]
df = df.T.apply(lambda arr: shift_null(arr)).T

CodePudding user response:

You can remove missing values per rows by Series.dropna, add possible only missing values columns by DataFrame.reindex and then set columns names by list by DataFrame.set_axis:

cols = ['A','B','C','D','E']
df[cols] = (df[cols].apply(lambda x: pd.Series(x.dropna().tolist()), axis=1)
                   .reindex(range(len(cols)), axis=1)
                   .set_axis(cols, axis=1))
print (df)
     A     B     C     D   E    F    G
0  A.1  Data  Data   NaN NaN  223   52
1  A.2  Data  Data  Data NaN   12    6
2  A.4  Data   NaN   NaN NaN   32  365
3  A.5  Data   NaN   NaN NaN  100   88
4  A.6  Data   NaN   NaN NaN  654   98
5  A.7  Data   NaN   NaN NaN  356   56

Your solution is changed with remove transposing and result_type='expand' in DataFrame.apply:

cols = ['A','B','C','D','E']


def shift_null(arr):
    return [x for x in arr if x == x]   [np.nan for x in arr if x != x]
df[cols] = df[cols].apply(lambda arr: shift_null(arr), axis=1, result_type='expand')

print (df)
     A     B     C     D   E    F    G
0  A.1  Data  Data   NaN NaN  223   52
1  A.2  Data  Data  Data NaN   12    6
2  A.4  Data   NaN   NaN NaN   32  365
3  A.5  Data   NaN   NaN NaN  100   88
4  A.6  Data   NaN   NaN NaN  654   98
5  A.7  Data   NaN   NaN NaN  356   56

Another idea is sorting by key parameter:

cols = ['A','B','C','D','E']
df[cols] = df[cols].apply(lambda x: x.sort_values(key=lambda x: x.isna()).tolist(), 
                          axis=1, result_type='expand')
print (df)
     A     B     C     D   E    F    G
0  A.1  Data  Data   NaN NaN  223   52
1  A.2  Data  Data  Data NaN   12    6
2  A.4  Data   NaN   NaN NaN   32  365
3  A.5  Data   NaN   NaN NaN  100   88
4  A.6  Data   NaN   NaN NaN  654   98
5  A.7  Data   NaN   NaN NaN  356   56

Solution with reshape by DataFrame.stack, add counter for new columns names and last reshape back by Series.unstack:

s = df[cols].stack().droplevel(1)
s.index = [s.index, s.groupby(level=0).cumcount()]
df[cols] = s.unstack().rename(dict(enumerate(cols)), axis=1).reindex(cols, axis=1)
print (df)
     A     B     C     D   E    F    G
0  A.1  Data  Data   NaN NaN  223   52
1  A.2  Data  Data  Data NaN   12    6
2  A.4  Data   NaN   NaN NaN   32  365
3  A.5  Data   NaN   NaN NaN  100   88
4  A.6  Data   NaN   NaN NaN  654   98
5  A.7  Data   NaN   NaN NaN  356   56
  • Related