Left justify string values in a pandas DataFrame

Solution 1:

First, extract a slice of columns beginning with item -

m = df.columns.str.contains('item')
i = df.iloc[:, m]

Mask all values which meet your criteria. Use isin -

j = i[~i.isin(df.makrc.tolist() + ['not'])]

Now. sort values based on NaNs and assign back -

df.loc[:, m] = j.apply(sorted, key=pd.isnull, axis=1)
df

    key  sellyr brand makrc item1 item2  item3  item4  item5  item6
0  da12    2013   imp   apt  furi   NaN    NaN    NaN    NaN    NaN
1  da32    2013    sa   rye   app   NaN    NaN    NaN    NaN    NaN
2  da14    2013    sa   pro   pan   fan    NaN    NaN    NaN    NaN

Details

i

  item1 item2 item3 item4  item5  item6
0  furi   apt   NaN   NaN    NaN    NaN
1   rye   app   NaN   NaN    NaN    NaN
2   not   pro   pan   fan    NaN    NaN
j

  item1 item2 item3 item4  item5  item6
0  furi   NaN   NaN   NaN    NaN    NaN
1   NaN   app   NaN   NaN    NaN    NaN
2   NaN   NaN   pan   fan    NaN    NaN

Toward Better Performance

You could make use of a modified version of Divakar's justified function that works on object arrays -

def justify(a, invalid_val=0, axis=1, side='left'):    
    """
    Justifies a 2D array

    Parameters
    ----------
    A : ndarray
        Input array to be justified
    axis : int
        Axis along which justification is to be made
    side : str
        Direction of justification. It could be 'left', 'right', 'up', 'down'
        It should be 'left' or 'right' for axis=1 and 'up' or 'down' for axis=0.

    """

    if invalid_val is np.nan:
        mask = pd.notnull(a)
    else:
        mask = a!=invalid_val
    justified_mask = np.sort(mask,axis=axis)
    if (side=='up') | (side=='left'):
        justified_mask = np.flip(justified_mask,axis=axis)
    out = np.full(a.shape, invalid_val, dtype=object) 
    if axis==1:
        out[justified_mask] = a[mask]
    else:
        out.T[justified_mask.T] = a.T[mask.T]
    return out
df.loc[:, m] = justify(j.values, invalid_val=np.nan, axis=1, side='left')
df

    key  sellyr brand makrc item1 item2  item3  item4  item5  item6
0  da12    2013   imp   apt  furi   NaN    NaN    NaN    NaN    NaN
1  da32    2013    sa   rye   app   NaN    NaN    NaN    NaN    NaN
2  da14    2013    sa   pro   pan   fan    NaN    NaN    NaN    NaN

This should (hopefully) be faster than calling apply. You'll especially see speed gains using the original version of the function that is optimised for numeric data.