分组并根据熊猫中的多种条件计算计数和均值

时间:2020-03-12 05:24:28

标签: python-3.x pandas dataframe

对于给定的数据帧,如下所示:

  id|address|sell_price|market_price|status|start_date|end_date
  1|7552 Atlantic Lane|1170787.3|1463484.12|finished|2019/8/2|2019/10/1
  1|7552 Atlantic Lane|1137782.02|1422227.52|finished|2019/8/2|2019/10/1
  2|888 Foster Street|1066708.28|1333385.35|finished|2019/8/2|2019/10/1
  2|888 Foster Street|1871757.05|1416757.05|finished|2019/10/14|2019/10/15
  2|888 Foster Street|NaN|763744.52|current|2019/10/12|2019/10/13
  3|5 Pawnee Avenue|NaN|928366.2|current|2019/10/10|2019/10/11
  3|5 Pawnee Avenue|NaN|2025924.16|current|2019/10/10|2019/10/11
  3|5 Pawnee Avenue|Nan|4000000|forward|2019/10/9|2019/10/10
  3|5 Pawnee Avenue|2236138.9|1788938.9|finished|2019/10/8|2019/10/9
  4|916 W. Mill Pond St.|2811026.73|1992026.73|finished|2019/9/30|2019/10/1
  4|916 W. Mill Pond St.|13664803.02|10914803.02|finished|2019/9/30|2019/10/1
  4|916 W. Mill Pond St.|3234636.64|1956636.64|finished|2019/9/30|2019/10/1
  5|68 Henry Drive|2699959.92|NaN|failed|2019/10/8|2019/10/9
  5|68 Henry Drive|5830725.66|NaN|failed|2019/10/8|2019/10/9
  5|68 Henry Drive|2668401.36|1903401.36|finished|2019/12/8|2019/12/9

#copy above data and run below code to reproduce dataframe
df = pd.read_clipboard(sep='|') 

我想对idaddress进行分组,并根据以下条件计算mean_ratioresult_count

  1. mean_ratio:由idaddress进行分组,并计算行的均值满足以下条件:statusfinished和{{1} }在start_date2019-09的范围内
  2. 2019-10:由result_countid进行分组并计算行是否满足以下条件:addressstatusfinished ,并且failedstart_date2019-09的范围内

所需的输出将如下所示:

2019-10

到目前为止,我已经尝试过:

   id               address  mean_ratio  result_count
0   1    7552 Atlantic Lane         NaN             0
1   2     888 Foster Street        1.32             1
2   3       5 Pawnee Avenue        1.25             1
3   4  916 W. Mill Pond St.        1.44             3
4   5        68 Henry Drive         NaN             2

要过滤# convert date df[['start_date', 'end_date']] = df[['start_date', 'end_date']].apply(lambda x: pd.to_datetime(x, format = '%Y/%m/%d')) # calculate ratio df['ratio'] = round(df['sell_price']/df['market_price'], 2) 的范围是start_date2019-09

2019-10

要过滤行状态为L = [pd.Period('2019-09'), pd.Period('2019-10')] c = ['start_date'] df = df[np.logical_or.reduce([df[x].dt.to_period('m').isin(L) for x in c])] finished,我使用:

failed

但是我不知道如何使用那些来获得最终结果。谢谢您的帮助。

2 个答案:

答案 0 :(得分:2)

我认为您需要GroupBy.agg,但是由于排除了某些行,例如id=1,所以请按DataFrame.join加上所有唯一对idaddress来添加在df2中,最后替换result_count列中的缺失值:

df2 = df[['id','address']].drop_duplicates()
print (df2)
    id               address
0    1    7552 Atlantic Lane
2    2     888 Foster Street
5    3       5 Pawnee Avenue
9    4  916 W. Mill Pond St.
12   5        68 Henry Drive

df[['start_date', 'end_date']] = df[['start_date', 'end_date']].apply(lambda x: pd.to_datetime(x, format = '%Y/%m/%d'))
df['ratio'] = round(df['sell_price']/df['market_price'], 2)
L = [pd.Period('2019-09'), pd.Period('2019-10')] 
c = ['start_date']

mask = df['status'].str.contains('finished|failed')
mask1 = np.logical_or.reduce([df[x].dt.to_period('m').isin(L) for x in c])

df = df[mask1 & mask]

df1 = df.groupby(['id', 'address']).agg(mean_ratio=('ratio','mean'),
                                        result_count=('ratio','size'))

df1 = df2.join(df1, on=['id','address']).fillna({'result_count': 0})
print (df1)
    id               address  mean_ratio  result_count
0    1    7552 Atlantic Lane         NaN           0.0
2    2     888 Foster Street    1.320000           1.0
5    3       5 Pawnee Avenue    1.250000           1.0
9    4  916 W. Mill Pond St.    1.436667           3.0
12   5        68 Henry Drive         NaN           2.0

答案 1 :(得分:1)

一些助手

def mean_ratio(idf):
    # filtering data
    idf = idf[
              (idf['start_date'].between('2019-09-01', '2019-10-31')) & 
              (idf['mean_ratio'].notnull()) ]
    return np.round(idf['mean_ratio'].mean(), 2)

def result_count(idf):
    idf = idf[
              (idf['status'].isin(['finished', 'failed'])) & 
              (idf['start_date'].between('2019-09-01', '2019-10-31')) ]
    return idf.shape[0]


# We can caluclate `mean_ratio` before hand
df['mean_ratio'] = df['sell_price'] / df['market_price']

df = df.astype({'start_date': np.datetime64, 'end_date': np.datetime64})

# Group the df
g =  df.groupby(['id', 'address'])

mean_ratio = g.apply(lambda idf: mean_ratio(idf)).to_frame('mean_ratio')
result_count = g.apply(lambda idf: result_count(idf)).to_frame('result_count')

# Final result
pd.concat((mean_ratio, result_count), axis=1)