Hexahedron
Hexahedron

Reputation: 31

Shifting values to the top in a Python DataFrame if NaN values are encountered

I am working with an irregular df. I am trying to get rid of the initial NaNs and shift all values to the top leaving NaNs at the bottom. I want to perform a realignment of the value at the top which ignores the date.

==================================================================

STRIP      col1  col2   col3  col4  col5  col6  col7 col8
01/12/2011  0.8   NaN   NaN   NaN   NaN   NaN   NaN  NaN
01/01/2012  0.8   0.8   NaN   NaN   NaN   NaN   NaN  NaN
01/02/2012  0.8   0.8   0.78  NaN   NaN   NaN   NaN  NaN
01/03/2012  0.8   0.8   0.75  0.7   0.6   NaN   NaN  NaN
01/04/2012  0.7   0.7   0.73  0.7   0.6   0.6   NaN  NaN
01/05/2012  0.7   0.7   0.72  0.7   0.6   0.6   0.1  NaN
01/06/2012  0.7   0.7   0.70  0.7   0.6   0.6   0.2  0.7
01/07/2012  0.7   0.7   0.69  0.7   0.6   0.6   0.3  0.9
01/08/2012  0.7   0.7   0.68  0.7   0.6   0.6   0.4  0.6
01/09/2012  0.7   0.7   0.67  0.7   0.6   0.6   0.5  0.4
02/01/2013  NaN   NaN   NaN   NaN   0.5   0.6   0.8  0.3
03/01/2013  NaN   NaN   NaN   NaN   0.5   0.6   0.7  0.2

===================================================================
the final DataFrame should look like the following:

STRIP      col1  col2  col3  col4  col5  col6  col7  col8
01/12/2011  0.8  0.8   0.78  0.7   0.6   0.6    0.1  0.7
01/01/2012  0.8  0.8   0.75  0.7   0.6   0.6    0.2  0.9
01/02/2012  0.8  0.8   0.73  0.7   0.6   0.6    0.3  0.6
01/03/2012  0.8  0.7   0.72  0.7   0.6   0.6    0.4  0.4
01/04/2012  0.7  0.7   0.7   0.7   0.6   0.6    0.5  0.3
01/05/2012  0.7  0.7   0.69  0.7   0.6   0.6    0.6  0.2
01/06/2012  0.7  0.7   0.68  0.7   0.6   0.6    0.7  NaN
01/07/2012  0.7  0.7   0.67  NaN   0.5   0.6    NaN  NaN
01/08/2012  0.7  0.7   NaN   NaN   0.5   NaN    NaN  NaN
01/09/2012  0.7  NaN   NaN   NaN   NaN   NaN    NaN  NaN
02/01/2013  NaN  NaN   NaN   NaN   NaN   NaN    NaN  NaN
03/01/2013  NaN  NaN   NaN   NaN   NaN   NaN    NaN  NaN

Upvotes: 3

Views: 93

Answers (2)

CT Zhu
CT Zhu

Reputation: 54340

I think you can just stack the valid numbers and nan's back together:

In [95]:

df2 = df.apply(lambda x: np.hstack((x[~x.isnull()], x[x.isnull()])), axis=0)
print df2

         STRIP  col1  col2  col3  col4  col5  col6  col7  col8
0   01/12/2011   0.8   0.8  0.78   0.7   0.6   0.6   0.1   0.7
1   01/01/2012   0.8   0.8  0.75   0.7   0.6   0.6   0.2   0.9
2   01/02/2012   0.8   0.8  0.73   0.7   0.6   0.6   0.3   0.6
3   01/03/2012   0.8   0.7  0.72   0.7   0.6   0.6   0.4   0.4
4   01/04/2012   0.7   0.7  0.70   0.7   0.6   0.6   0.5   0.3
5   01/05/2012   0.7   0.7  0.69   0.7   0.6   0.6   0.8   0.2
6   01/06/2012   0.7   0.7  0.68   0.7   0.6   0.6   0.7   NaN
7   01/07/2012   0.7   0.7  0.67   NaN   0.5   0.6   NaN   NaN
8   01/08/2012   0.7   0.7   NaN   NaN   0.5   NaN   NaN   NaN
9   01/09/2012   0.7   NaN   NaN   NaN   NaN   NaN   NaN   NaN
10  02/01/2013   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN
11  03/01/2013   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN

Upvotes: 1

EdChum
EdChum

Reputation: 394099

You iterate over the cols and using first_valid_index and get_loc shift the col values:

In [314]:
for col in df:
    df[col] = df[col].shift(-df.index.get_loc(df[col].first_valid_index()))
df

Out[314]:
            col1  col2  col3  col4  col5  col6  col7  col8
STRIP                                                     
01/12/2011   0.8   0.8  0.78   0.7   0.6   0.6   0.1   0.7
01/01/2012   0.8   0.8  0.75   0.7   0.6   0.6   0.2   0.9
01/02/2012   0.8   0.8  0.73   0.7   0.6   0.6   0.3   0.6
01/03/2012   0.8   0.7  0.72   0.7   0.6   0.6   0.4   0.4
01/04/2012   0.7   0.7  0.70   0.7   0.6   0.6   0.5   0.3
01/05/2012   0.7   0.7  0.69   0.7   0.6   0.6   0.8   0.2
01/06/2012   0.7   0.7  0.68   0.7   0.6   0.6   0.7   NaN
01/07/2012   0.7   0.7  0.67   NaN   0.5   0.6   NaN   NaN
01/08/2012   0.7   0.7   NaN   NaN   0.5   NaN   NaN   NaN
01/09/2012   0.7   NaN   NaN   NaN   NaN   NaN   NaN   NaN
02/01/2013   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN
03/01/2013   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN

Another method using apply:

In [317]:
df.apply(lambda x: x.shift(-x.index.get_loc(x.first_valid_index())))

Out[317]:
            col1  col2  col3  col4  col5  col6  col7  col8
STRIP                                                     
01/12/2011   0.8   0.8  0.78   0.7   0.6   0.6   0.1   0.7
01/01/2012   0.8   0.8  0.75   0.7   0.6   0.6   0.2   0.9
01/02/2012   0.8   0.8  0.73   0.7   0.6   0.6   0.3   0.6
01/03/2012   0.8   0.7  0.72   0.7   0.6   0.6   0.4   0.4
01/04/2012   0.7   0.7  0.70   0.7   0.6   0.6   0.5   0.3
01/05/2012   0.7   0.7  0.69   0.7   0.6   0.6   0.8   0.2
01/06/2012   0.7   0.7  0.68   0.7   0.6   0.6   0.7   NaN
01/07/2012   0.7   0.7  0.67   NaN   0.5   0.6   NaN   NaN
01/08/2012   0.7   0.7   NaN   NaN   0.5   NaN   NaN   NaN
01/09/2012   0.7   NaN   NaN   NaN   NaN   NaN   NaN   NaN
02/01/2013   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN
03/01/2013   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN

EDIT

If 'STRIP' is a column then you don't need get_loc:

In [319]:
df.apply(lambda x: x.shift(-x.first_valid_index()))

Out[319]:
         STRIP  col1  col2  col3  col4  col5  col6  col7  col8
0   01/12/2011   0.8   0.8  0.78   0.7   0.6   0.6   0.1   0.7
1   01/01/2012   0.8   0.8  0.75   0.7   0.6   0.6   0.2   0.9
2   01/02/2012   0.8   0.8  0.73   0.7   0.6   0.6   0.3   0.6
3   01/03/2012   0.8   0.7  0.72   0.7   0.6   0.6   0.4   0.4
4   01/04/2012   0.7   0.7  0.70   0.7   0.6   0.6   0.5   0.3
5   01/05/2012   0.7   0.7  0.69   0.7   0.6   0.6   0.8   0.2
6   01/06/2012   0.7   0.7  0.68   0.7   0.6   0.6   0.7   NaN
7   01/07/2012   0.7   0.7  0.67   NaN   0.5   0.6   NaN   NaN
8   01/08/2012   0.7   0.7   NaN   NaN   0.5   NaN   NaN   NaN
9   01/09/2012   0.7   NaN   NaN   NaN   NaN   NaN   NaN   NaN
10  02/01/2013   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN
11  03/01/2013   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN

Upvotes: 1

Related Questions