172

Using this as a starting point:

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

which looks like

  one  two three
0   10  1.2   4.2
1   15  70   0.03
2    8   5     0

I want to use something like an if statement within pandas.

if df['one'] >= df['two'] and df['one'] <= df['three']:
    df['que'] = df['one']

Basically, create a new column by checking each row via the if statement.

The docs say to use .all but there is no example...

cottontail
  • 10,268
  • 18
  • 50
  • 51
Merlin
  • 24,552
  • 41
  • 131
  • 206

13 Answers13

216

You could use np.where. If cond is a boolean array, and A and B are arrays, then

C = np.where(cond, A, B)

defines C to be equal to A where cond is True, and B where cond is False.

import numpy as np
import pandas as pd

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

df['que'] = np.where((df['one'] >= df['two']) & (df['one'] <= df['three'])
                     , df['one'], np.nan)

yields

  one  two three  que
0  10  1.2   4.2   10
1  15   70  0.03  NaN
2   8    5     0  NaN

If you have more than one condition, then you could use np.select instead. For example, if you wish df['que'] to equal df['two'] when df['one'] < df['two'], then

conditions = [
    (df['one'] >= df['two']) & (df['one'] <= df['three']), 
    df['one'] < df['two']]

choices = [df['one'], df['two']]

df['que'] = np.select(conditions, choices, default=np.nan)

yields

  one  two three  que
0  10  1.2   4.2   10
1  15   70  0.03   70
2   8    5     0  NaN

If we can assume that df['one'] >= df['two'] when df['one'] < df['two'] is False, then the conditions and choices could be simplified to

conditions = [
    df['one'] < df['two'],
    df['one'] <= df['three']]

choices = [df['two'], df['one']]

(The assumption may not be true if df['one'] or df['two'] contain NaNs.)


Note that

a = [['10', '1.2', '4.2'], ['15', '70', '0.03'], ['8', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])

defines a DataFrame with string values. Since they look numeric, you might be better off converting those strings to floats:

df2 = df.astype(float)

This changes the results, however, since strings compare character-by-character, while floats are compared numerically.

In [61]: '10' <= '4.2'
Out[61]: True

In [62]: 10 <= 4.2
Out[62]: False
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
127

You can use .equals to compare 2 columns:

df['col1'].equals(df['col2'])

or to compare 2 dataframes:

df1.equals(df2)

If they're equal, that statement will return True, else False.

blackraven
  • 5,284
  • 7
  • 19
  • 45
ccook5760
  • 1,330
  • 1
  • 8
  • 6
34

You could use apply() and do something like this

df['que'] = df.apply(lambda x : x['one'] if x['one'] >= x['two'] and x['one'] <= x['three'] else "", axis=1)

or if you prefer not to use a lambda

def que(x):
    if x['one'] >= x['two'] and x['one'] <= x['three']:
        return x['one']
    return ''
df['que'] = df.apply(que, axis=1)
divykj
  • 576
  • 6
  • 12
Bob Haffner
  • 8,235
  • 1
  • 36
  • 43
13

One way is to use a Boolean series to index the column df['one']. This gives you a new column where the True entries have the same value as the same row as df['one'] and the False values are NaN.

The Boolean series is just given by your if statement (although it is necessary to use & instead of and):

>>> df['que'] = df['one'][(df['one'] >= df['two']) & (df['one'] <= df['three'])]
>>> df
    one two three   que
0   10  1.2 4.2      10
1   15  70  0.03    NaN
2   8   5   0       NaN

If you want the NaN values to be replaced by other values, you can use the fillna method on the new column que. I've used 0 instead of the empty string here:

>>> df['que'] = df['que'].fillna(0)
>>> df
    one two three   que
0   10  1.2   4.2    10
1   15   70  0.03     0
2    8    5     0     0
Alex Riley
  • 169,130
  • 45
  • 262
  • 238
9

Wrap each individual condition in parentheses, and then use the & operator to combine the conditions:

df.loc[(df['one'] >= df['two']) & (df['one'] <= df['three']), 'que'] = df['one']

You can fill the non-matching rows by just using ~ (the "not" operator) to invert the match:

df.loc[~ ((df['one'] >= df['two']) & (df['one'] <= df['three'])), 'que'] = ''

You need to use & and ~ rather than and and not because the & and ~ operators work element-by-element.

The final result:

df
Out[8]: 
  one  two three que
0  10  1.2   4.2  10
1  15   70  0.03    
2   8    5     0  
Marius
  • 58,213
  • 16
  • 107
  • 105
7

I'd like to add this answer for those who are trying to compare the equality of values in two columns that have NaN values, and get False when both values are NaN. By definition, NaN != NaN (See: numpy.isnan(value) not the same as value == numpy.nan?).

If you want the two NaN comparison to return True, you can use:

df['compare'] = (df["col_1"] == df["col_2"]) | (df["col_1"].isna() & df["col_2"].isna())
Rafael L
  • 71
  • 1
  • 4
4

Use lambda expression:

df[df.apply(lambda x: x['col1'] != x['col2'], axis = 1)]
aze45sq6d
  • 876
  • 3
  • 11
  • 26
3

Use np.select if you have multiple conditions to be checked from the dataframe and output a specific choice in a different column

conditions=[(condition1),(condition2)]
choices=["choice1","chocie2"]

df["new column"]=np.select=(condtion,choice,default=)

Note: No of conditions and no of choices should match, repeat text in choice if for two different conditions you have same choices

Dharman
  • 30,962
  • 25
  • 85
  • 135
psn1997
  • 144
  • 9
2

You can use the method where:

df['que'] = df['one'].where((df['one'] >= df['two']) & (df['one'] <= df['three']))

or the method eval:

df['que'] = df.loc[df.eval('(one >= two) & (two <= three)'), 'one']

Result:

  one  two three  que
0  10  1.2   4.2   10
1  15   70  0.03  NaN
2   8    5     0  NaN
Mykola Zotko
  • 15,583
  • 3
  • 71
  • 73
0

I think the closest to the OP's intuition is an inline if statement:

df['que'] = (df['one'] if ((df['one'] >= df['two']) and (df['one'] <= df['three'])) 
Nic Scozzaro
  • 6,651
  • 3
  • 42
  • 46
0

If you're here to compare values in two dataframe columns, you can use eq():

df['one'].eq(df['two'])

or eval()

df.eval("one == two")

and if you want to reduce it to a single boolean, call all() on the result:

df['one'].eq(df['two']).all()
# or
df.eval("one == two").all()

This is a more "robust" check than equals() because for equals() to return True, the column dtypes must match as well. So if one column is dtype int and the other is dtype float, equals() would return False even if the values are the same, whereas eq().all()/eval().all() simply compares the columns element-wise.

If your columns includes NaN values, then use the following (which leverages the fact that NaN != NaN):

df.eval("one == two or one != one").all()

For OP's specific question, since the pattern is "A < B and B < C", you can use between():

cond = df['one'].between(df['two'], df['three'])
df['que'] = np.where(cond, df['one'], np.nan)
cottontail
  • 10,268
  • 18
  • 50
  • 51
0

To elaborate on @ccook5760's answer

You can use .equals for columns or entire dataframes.

df['col1'].equals(df['col2'])

If they're equal, that statement will return True, else False.

For the equality to be verified, the columns must contain the same values in the same order and their indexes must be identical too.

If you wanted to check equality of two columns from two different dataframes where order of values is not important and may vary, you can sort the values first.

It is also important to reset the index of the series so that the equality can be verified based only on the values.

Here is one way to do it :

df1['col1'].sort_values().reset_index(drop=True).equals(df2['col2'].sort_values().reset_index(drop=True))

Same method, in a more readable way :

s1 = df1['col1'].sort_values().reset_index(drop=True)
s2 = df2['col2'].sort_values().reset_index(drop=True)
s1.equals(s2)
leas
  • 329
  • 8
  • 17
0

if your columns contain the same values in the same order and they are numeric then use equality else use .equals method

col1 contains: 58.1.2 , 29.2.4 col2 contains: 58.1.2 , 28.2.4

syntax: mydf['processedRecords'] = (mydf['col1'] == mydf['col2'])

so when it will match you will get True And when it won't match you will get False

but the values are non-numeric go for .equals method

syntax: mydf['processedRecords'] = mydf['col1'].equals(mydf['col2'])