jth359
jth359

Reputation: 909

Pandas: Column that is dependent on another value

I have a Pandas dataframe like the following:

   col1  col2  col3  col4
0     5     1    11     9
1     2     3    14     7
2     6     5    54     8
3    11     2    67    44
4    23     8     2    23
5     1     5     9     8
6     9     7    45    71

I want to make a 5th column (col5) that is dependent on the value of col1 and takes a value of one of the other columns.

Here's kind of how I want it to look, but am having some issues.

if col1 < 3:
   col5 == col2
elif col1 < 7 & col1 >= 3:
   col5 == col3
elif col1 >= 7 & col1 < 50:
   col5 == col4

Which would produce the following dataframe:

   col1  col2  col3  col4  col5
0     5     1    11     9    11
1     2     3    14     7     3
2     6     5    54     8    54
3    11     2    67    44    44
4    23     8     2    23    23
5    97     5     9     8     8
6     9     7    45    71    71

Thanks in advance and let me know if you have any questions

Upvotes: 3

Views: 4389

Answers (2)

jezrael
jezrael

Reputation: 863501

You can use multiple numpy.where, if no condition is True (col1 => 50) was added last value 1:

df['col5'] = np.where(df['col1'] <3, df['col2'], 
             np.where((df['col1'] <7) & (df['col1'] >=3 ), df['col3'], 
             np.where((df['col1'] >=7) & (df['col1'] <50 ), df['col4'], 1))) 
print (df)
   col1  col2  col3  col4  col5
0     5     1    11     9    11
1     2     3    14     7     3
2     6     5    54     8    54
3    11     2    67    44    44
4    23     8     2    23    23
5    97     5     9     8     1
6     9     7    45    71    71

EDIT by changed values:

If need col4 for all values >=7:

df['col5'] = np.where(df['col1'] <3, df['col2'], 
             np.where((df['col1'] <7) & (df['col1'] >=3 ), df['col3'], df['col4']))
print (df)
   col1  col2  col3  col4  col5
0     5     1    11     9    11
1     2     3    14     7     3
2     6     5    54     8    54
3    11     2    67    44    44
4    23     8     2    23    23
5    97     5     9     8     8
6     9     7    45    71    71

Timings in len(df)=7000:

In [441]: %timeit df['col51'] = np.where(df['col1'] <3, df['col2'], np.where((df['col1'] <7) & (df['col1'] >=3 ), df['col3'], df['col4']))
The slowest run took 5.31 times longer than the fastest. This could mean that an intermediate result is being cached.
1000 loops, best of 3: 1.25 ms per loop

In [442]: %timeit df["col52"] = df.apply(lambda x: col52(x), axis=1)
1 loop, best of 3: 552 ms per loop

In [443]: %timeit df["col53"] = [col53(c1,c2,c3,c4) for c1,c2,c3,c4 in zip(df.col1,df.col2,df.col3,df.col4)]
100 loops, best of 3: 9.87 ms per loop

Timings in len(df)=70k

In [446]: %timeit df['col51'] = np.where(df['col1'] <3, df['col2'], np.where((df['col1'] <7) & (df['col1'] >=3 ), df['col3'], df['col4']))
100 loops, best of 3: 2.5 ms per loop

In [447]: %timeit df["col52"] = df.apply(lambda x: col52(x), axis=1)
1 loop, best of 3: 5.36 s per loop

In [448]: %timeit df["col53"] = [col53(c1,c2,c3,c4) for c1,c2,c3,c4 in zip(df.col1,df.col2,df.col3,df.col4)]
10 loops, best of 3: 96.3 ms per loop

Code for timings:

#change 1000 to 10000 for 70k
df = pd.concat([df]*1000).reset_index(drop=True)

def col52(x):
    if x["col1"] < 3:
        return x["col2"]
    elif x["col1"] >=3 and x["col1"] < 7:
        return x["col3"]
    elif x["col1"] >= 7 and x["col1"] < 50:
        return x["col4"] 
def col53(c1,c2,c3,c4):
    if c1 < 3:
        return c2
    elif c1 >=3 and c1 < 7:
        return c3
    elif c1>= 7 and c1< 50:
        return c4    

df['col51'] = np.where(df['col1'] <3, df['col2'], np.where((df['col1'] <7) & (df['col1'] >=3 ), df['col3'], df['col4']))       
df["col52"] = df.apply(lambda x: col52(x), axis=1)
df["col53"] = [col53(c1,c2,c3,c4) for c1,c2,c3,c4 in zip(df.col1,df.col2,df.col3,df.col4)]
print (df)

Upvotes: 7

Falcon9
Falcon9

Reputation: 166

One way to do this is to use the pd.DataFrame.apply function:

    def col5(x):
        if x["col1"] < 3:
            return x["col2"]
        elif x["col1"] >=3 and x["col1"] < 7:
            return x["col3"]
        elif x["col1"] >= 7 and x["col1"] < 50:
            return x["col4"]              

This function takes each row of the dataframe as input. You can access the various column values in that row in the usual way: x["col1"], x["col2"], etc.

Now, use the apply function which is used to run a custom function (like col5 above) over each row or column. The argument "axis" is 1 for applying the function to each row and is 0 for applying the function to each column (in which case, x has a different structure i.e. it's indexed by your index not column names):

    df["col5"] = df.apply(lambda x: col5(x), axis=1)

Also, as a general note, apply can be very slow specially when you have functions with if-else blocks because for each row, your processor has to decide which statement in the if-else block should be executed ("pipelining" and "branch prediction"). You should be okay here though.

Upvotes: 3

Related Questions