使用pandas基于COUNTIF()的单独的Excel表build立一个二维表

我想build立一个基于值(和countifs)从另一个表的二维表。 我设法成功地使用Excel原型,但是我坚持两个概念:

1. Emulating Excel COUNTIF() on pandas 2. Dynamically build a new dataframe 

注意:COUNTIF()将范围和标准作为参数。 例如,如果我有一个颜色列表,我想知道下面的列表中的“橙色”的次数:

 A Red Orange Blue Orange Black 

,那么我会简单地使用下面的公式:

 COUNTIF(A1:A5, "Orange") 

这应该返回2。

当然,COUNTIF()函数可以变得更加复杂,比如COUNTIF(range1,criterion1,range2,criterion2 …)这种forms的表单连接条件可以被解释为一个AND标准。 例如,如果我想在类似于下面的列表中计算35岁以上的女性:

 AB Female 19 Female 40 Male 45 

,那么我会简单地使用下面的公式:

 COUNTIF(A1:A3, "Female", B1:B3, ">35" 

这应该返回1。

回到我的用例。 这是源表:

  Product No Opening Date Closing Date Opening Month Closing Month 0 1 2016-01-01 2016-06-30 2016-01-31 2016-06-30 1 2 2016-01-01 2016-04-30 2016-01-31 2016-04-30 2 3 2016-02-01 2016-06-30 2016-02-29 2016-06-30 3 4 2016-02-01 2016-05-31 2016-02-29 2016-05-31 4 5 2016-02-01 2099-12-31 2016-02-29 2099-12-31 5 6 2016-01-01 2099-12-31 2016-01-31 2016-10-31 6 7 2016-06-01 2016-07-31 2016-06-30 2016-07-31 7 8 2016-06-01 2016-11-30 2016-06-30 2016-11-30 8 9 2016-06-01 2016-07-31 2016-06-30 2016-07-31 9 10 2016-06-01 2099-12-31 2016-06-30 2099-12-31 

这是我想要实现的2Dmatrix:

  2016-01-31 2016-02-29 2016-03-31 2016-04-30 2016-05-31 \ 2016-01-31 3 3 3 2 2 2016-02-29 3 3 3 3 2 2016-03-31 0 0 0 0 0 2016-04-30 0 0 0 0 0 2016-05-31 0 0 0 0 0 2016-06-30 4 4 4 4 4 2016-07-31 0 0 0 0 0 2016-08-31 0 0 0 0 0 2016-09-30 0 0 0 0 0 2016-10-31 0 0 0 0 0 2016-11-30 0 0 0 0 0 2016-12-31 0 0 0 0 0 2016-06-30 2016-07-31 2016-08-31 2016-09-30 2016-10-31 \ 2016-01-31 1 1 1 1 0 2016-02-29 1 1 1 1 1 2016-03-31 0 0 0 0 0 2016-04-30 0 0 0 0 0 2016-05-31 0 0 0 0 0 2016-06-30 4 2 2 2 2 2016-07-31 0 0 0 0 0 2016-08-31 0 0 0 0 0 2016-09-30 0 0 0 0 0 2016-10-31 0 0 0 0 0 2016-11-30 0 0 0 0 0 2016-12-31 0 0 0 0 0 2016-11-30 2016-12-31 2016-01-31 0 0 2016-02-29 1 1 2016-03-31 0 0 2016-04-30 0 0 2016-05-31 0 0 2016-06-30 1 1 2016-07-31 0 0 2016-08-31 0 0 2016-09-30 0 0 2016-10-31 0 0 2016-11-30 0 0 2016-12-31 0 0 

基本上我想要build立一个产品生存matrix。 纵轴是新产品的起源,横轴是衡量这些帐户在多大程度上持续下去的时间。

例如,如果1月份推出了10款产品,那么1月份和1月份的数字应该是10.如果10月份的产品中有1款在2月份closures了,那么1月份和2月份的数字应该是9月份。如果所有剩余产品在6月份之前closures那么1月到6月,7月,8月等行应该是0。

2月,3月,4月等产品开发将不会影响1月份的行情。

我设法使用以下excel公式构build二维matrix:

 =COUNTIF(Accounts!$D$2:$D$11,Main!$A2)-COUNTIFS(Accounts!$D$2:$D$11,Main!$A2, Accounts!$E$2:$E$11,"<="&Main!B$1) 

(这将填充第一个单元格)

我最初的策略是build立一个多维列表,并使用一些for循环来填充它们,但是我不确定在Pandas中是否有一个更容易的(或更推荐的方法)。

由于我现在还没有足够的声望来评论你的问题,所以我会假设你在你的数据中有年份等于2099的拼写错误。

我也想问一下,在你的2016-06-30排的前几列(即2016-01-31到2016-05-31)中,如何存在4个“产品编号”。

如果这些错误,那么这是我的解决scheme:

首先,制作数据:

 # Make dataframe df = pd.DataFrame({'Product No' : [i for i in range(1,11)], 'Opening Date' : ['2016-01-01']*2 +\ ['2016-02-01']*3 +\ ['2016-01-01'] +\ ['2016-06-01']*4, 'Closing Date' : ['2016-06-30', '2016-04-30', '2016-06-30', '2016-05-31'] +\ ['2016-12-31']*2 +\ ['2016-07-31', '2016-11-30', '2016-07-31', '2016-12-31'], 'Opening Month' : ['2016-01-31']*2 +\ ['2016-02-29']*3 +\ ['2016-01-31'] +\ ['2016-06-30']*4, 'Closing Month' : ['2016-06-30', '2016-04-30', '2016-06-30', '2016-05-31', '2016-12-31', '2016-10-31', '2016-07-31', '2016-11-30', '2016-07-31', '2016-12-31']}) # Reorder columns df = df.loc[:, ['Product No', 'Opening Date', 'Closing Date', 'Opening Month', 'Closing Month']] # Convert dates to datetime for i in df.columns[1:]: df.loc[:, i] = pd.to_datetime(df.loc[:, i]) 

其次,我创build了一个“date范围”数据框,用于保存原始数据集的最小值到最大值。 我还包括了“产品号”栏,以便每个产品在表格上都有一排:

 # Create date range dataframe daterange = pd.DataFrame({'daterange' : pd.date_range(start = df.loc[:, 'Opening Month'].min(), end = df.loc[:, 'Closing Month'].max(), freq = 'M'), 'Product No' : [1]*12}) # Create 10 multiples of the daterange and concatenate daterange10 = pd.concat([daterange]*10) # Find the cumulative sum of the 'Product No' for daterange10 daterange10.loc[:, 'Product No'] = daterange10.groupby('daterange').cumsum() 

第三,我将date范围和原始df合并在一起,并限制行仅在“产品编号”存在时才包括在内。 另外请注意,如果产品在本月的最后一天closures,那么封闭date必须大于或等于(从我的观点)以来的date范围,那么它在整个月份中存在:

 # Merge df with daterange10 df = df.merge(daterange10, how = 'inner', on = 'Product No') # Limit rows to when 'Opening Month' is <= 'daterange' and 'Closing Month' is >= 'daterange' df = df[(df.loc[:, 'Opening Month'] <= df.loc[:, 'daterange']) & (df.loc[:, 'Closing Month'] >= df.loc[:, 'daterange'])] 

最后,我用date值做一个数据透视表。 请注意,它只包含垂直轴上的date,

 # Pivot on 'Opening Month', 'daterange'; count unique 'Product No'; fill NA with 0 df.pivot_table(index = 'Opening Month', columns = 'daterange', values = 'Product No', aggfunc = pd.Series.nunique).fillna(0) 

数据透视表

尝试把你的数据放入一个pandasDataFrame,然后使用迭代的方法来构build产品生存的DataFrame:

 import pandas as pd mydata = pd.read_excel('mysourcedata.xlsx') def product_survival(sourcedf, startdate, enddate): df = pd.DataFrame() daterange = pd.date_range(startdate, enddate, freq='M') for i in daterange: # Rows for j in daterange: # Columns mycount = sourcedf[(sourcedf['Opening Month'] == i) & (sourcedf['Closing Month'] > j)]['Product No'].count() df.loc[i, j] = mycount return df print(product_survival(mydata, '2016-01-31', '2016-12-31'))