pandas根据同一行

时间:2018-01-22 16:26:25

标签: python python-3.x pandas dataframe

我有以下df

days    days_1    days_2    period    percent_1   percent_2    amount
3       5         4         1         0.2         0.1         100
2       1         3         4         0.3         0.1         500
9       8         10        6         0.4         0.2         600
10      7         8         11        0.5         0.3         700
10      5         6         7         0.7         0.4         800        

我正在尝试根据同一行中每列的值创建两个名为amount_misseddays_missed的新列,代码就像,

# init the two columns  
df['amount_missed'] = 0.0
df['days_missed'] = 0
# iter through each row to get values for the new columns 
# based on the other columns in the df
for row in df.itertuples():
    if getattr(row, 'days') < getattr(row, 'days_1'):
        df.loc[getattr(row, 'Index'), 'amount_missed'] = 0
        df.loc[getattr(row, 'Index'), 'days_missed'] = 0
    elif getattr(row, 'days_2') < getattr(row, 'days') < getattr(row, 'period') \
      or getattr(row, 'days') > getattr(row, 'period'):    
        missed_percent = getattr(row, 'percent_2')
        df.loc[getattr(row, 'Index'), 'amount_missed'] = getattr(row, 'amount') \
                                                      * (missed_percent / 100)
        df.loc[getattr(row, 'Index'), 'days_missed'] = getattr(row, 'days') \
                                                     - getattr(row, 'days_2')
    else:
        df.loc[getattr(row, 'Index'), 'amount_missed'] = 0
        df.loc[getattr(row, 'Index'), 'days_missed'] = 0

我想知道在pandas / numpy中是否有更简洁有效的方法。

UPDATE 结果df看起来像,

{'amount': {0: 100, 1: 500, 2: 600, 3: 700, 4: 800},
 'amount_missed': {0: 0.0, 1: 0.0, 2: 1.2, 3: 2.1, 4: 3.2},
 'days': {0: 3, 1: 2, 2: 9, 3: 10, 4: 10},
 'days_1': {0: 5, 1: 1, 2: 8, 3: 7, 4: 5},
 'days_2': {0: 4, 1: 3, 2: 10, 3: 8, 4: 6},
 'days_missed': {0: 0, 1: 0, 2: -1, 3: 2, 4: 4},
 'percent_1': {0: 0.2, 1: 0.3, 2: 0.4, 3: 0.5, 4: 0.7},
 'percent_2': {0: 0.1, 1: 0.1, 2: 0.2, 3: 0.3, 4: 0.4},
 'period': {0: 1, 1: 4, 2: 6, 3: 11, 4: 7}}

无法在df中正确格式化stackoverflow,因此必须to_dict

更新2 基于DYZ和Anton的答案,如果还有一个案例要考虑每一行,这使原始代码看起来像,

for row in df.itertuples():
    if getattr(row, 'days') < getattr(row, 'days_1'):
        df.loc[getattr(row, 'Index'), 'amount_missed'] = 0
        df.loc[getattr(row, 'Index'), 'days_missed'] = 0
    elif getattr(row, 'days_1') < getattr(row, 'days') < getattr(row, 'days_2'):
        missed_percent = getattr(row,'percent_1') - getattr(row,'percent_2')
        df.loc[getattr(row, 'Index'), 'amount'] = getattr(row, 'amount') * (missed_percent / 100)
        df.loc[getattr(row, 'Index'), 'days_missed'] = getattr(row, 'days') - getattr(row, 'days_1')    
    elif getattr(row, 'days_2') < getattr(row, 'days') < getattr(row, 'period') \
      or getattr(row, 'days') > getattr(row, 'period'):    
        missed_percent = getattr(row, 'percent_2')
        df.loc[getattr(row, 'Index'), 'amount_missed'] = getattr(row, 'amount') \
                                                  * (missed_percent / 100)
        df.loc[getattr(row, 'Index'), 'days_missed'] = getattr(row, 'days') \
                                                 - getattr(row, 'days_2')
    else:
        df.loc[getattr(row, 'Index'), 'amount_missed'] = 0
        df.loc[getattr(row, 'Index'), 'days_missed'] = 0    

使用下面建议的答案,我可以使它看起来如下吗?

cond1 = df['days_2'] < df['days']
cond2 = df['days'] < df['period']
cond3 = df['days'] > df['period']
cond4 = df['days'] >= df['days_1'] # The negation of df['days'] < df['days_1']
cond5 = df['days'] < df['days_2']
cond6 = df['days'] > df['days_1']

mask = ((cond1 & cond2) | cond3) & cond4
mask2 = cond5 & cond6

df['amount_missed'] = np.where(mask, df['amount'] * df['percent_2'] / 100, 0.0)
df['amount_missed'] = np.where(mask2, df['amount'] * (df['percent_1'] - df['percent_2']) / 100, 0.0)

df['days_missed'] = np.where(mask, df['days'] - df['days_2'], 0)
df['days_missed'] = np.where(mask2, df['days'] -df['days_1'], 0)

1 个答案:

答案 0 :(得分:3)

这是将您的代码直接翻译成适当的Pandas。通常,您不应该在数据框中按行使用循环。

# These rows are affected by the calculations
affected = ( ((df['days_2'] < df['days']) & (df['days'] < df['period']))\
            |(df['days'] > df['period'])) \
          &(df['days'] >= df['days_1']) # The negation of df['days'] < df['days_1']

# Explicitly insert non-zero calculated fields
df.loc[affected, 'amount_missed'] = df['amount'] * df['percent_2'] / 100
df.loc[affected, 'days_missed'] = df['days'] - df['days_2']

# Insert the missing zeros
df.fillna(0, inplace=True)

修改版(Anton vbr):

import pandas as pd
import numpy as np
import io

data = '''\
days    days_1    days_2    period    percent_1   percent_2    amount
3       5         4         1         0.2         0.1         100
2       1         3         4         0.3         0.1         500
9       8         10        6         0.4         0.2         600
10      7         8         11        0.5         0.3         700
10      5         6         7         0.7         0.4         800'''

df = pd.read_csv(io.StringIO(data), sep='\s+')

cond1 = df['days_2'] < df['days']
cond2 = df['days'] < df['period']
cond3 = df['days'] > df['period']
cond4 = df['days'] >= df['days_1'] # The negation of df['days'] < df['days_1']

mask = ((cond1 & cond2) | cond3) & cond4

df['amount_missed'] = np.where(mask, df['amount'] * df['percent_2'] / 100, 0.0)
df['days_missed'] = np.where(mask, df['days'] - df['days_2'], 0)