11

I was looking at this and this threads, and though my question is not so different, it has a few differences. I have a dataframe full of floats, that I want to replace by strings. Say:

      A     B       C
 A    0     1.5     13
 B    0.5   100.2   7.3
 C    1.3   34      0.01

To this table I want to replace by several criteria, but only the first replacement works:

df[df<1]='N' # Works
df[(df>1)&(df<10)]#='L' # Doesn't work
df[(df>10)&(df<50)]='M'  # Doesn't work
df[df>50]='H'  # Doesn't work

If I instead do the selection for the 2nd line based on float, still doesn't work:

((df.applymap(type)==float) & (df<10) & (df>1)) #Doesn't work

I was wondering how to apply pd.DataFrame().mask in here, or any other way. How should I solve this?

Alternatively, I know I may read column by column and apply the substitutions on each series, but this seems a bit counter productive

Edit: Could anyone explain why the 4 simple assignments above do not work?

Sos
  • 1,783
  • 2
  • 20
  • 46

3 Answers3

12

Use numpy.select with DataFrame constructor:

m1 = df < 1
m2 = (df>1)&(df<10)
m3 = (df>10)&(df<50)
m4 = df>5

vals = list('NLMH')

df = pd.DataFrame(np.select([m1,m2,m3,m4], vals), index=df.index, columns=df.columns)
print (df)
   A  B  C
A  N  L  M
B  N  H  L
C  L  M  N
jezrael
  • 822,522
  • 95
  • 1,334
  • 1,252
  • 1
    ah! I was really thinking about doing this, but wasn't sure how to use the 4 rules in a mask (I learned about `mask` from your previous answers on my posts a while back) – Sos May 30 '18 at 13:36
7

By using pd.cut

pd.cut(df.stack(),[-1,1,10,50,np.inf],labels=list('NLMH')).unstack()
Out[309]: 
   A  B  C
A  N  L  M
B  N  H  L
C  L  M  N
BENY
  • 317,841
  • 20
  • 164
  • 234
6

You can use searchsorted

Copy

labels = np.array(list('NLMH'))
breaks = np.array([1, 10, 50])
pd.DataFrame(
    labels[breaks.searchsorted(df.values)].reshape(df.shape),
    df.index, df.columns)

   A  B  C
A  N  L  M
B  N  H  L
C  L  M  N

In Place

labels = np.array(list('NLMH'))
breaks = np.array([1, 10, 50])
df[:] = labels[breaks.searchsorted(df.values)].reshape(df.shape)
df

   A  B  C
A  N  L  M
B  N  H  L
C  L  M  N

Chained pure Pandas approach with pandas.DataFrame.mask

Deprecated since version 0.21

df.mask(df.lt(1), 'N').mask(df.gt(1) & df.lt(10), 'L') \
  .mask(df.gt(10) & df.lt(50), 'M').mask(df.gt(50), 'H')

   A  B  C
A  N  L  M
B  N  H  L
C  L  M  N
piRSquared
  • 285,575
  • 57
  • 475
  • 624