在 Python Pandas DataFrame 的连续行上检测错误数据
Detect errant data over consecutive rows of a Python Pandas DataFrame
在对季度收益日期的 Pandas DataFrame 进行操作时,我意识到我想进行季度间(即 Q2 到 Q3)比较,我意识到我应该确定我的数据是订购正确且完整。
所以,
1. 总是 'correct' 季度 (q1->q2->q3->q4->q1->q2...)
2. 并且没有遗漏任何四分之一 (q1->q2->q4->q1->q2->q3) 不如遗漏 q3.
我已经加载了一个数据框,我认为在 pandas 中检查它比返回并检查预加载更有意义。
我想出了我认为是解决问题的两个稍微创可贴的解决方案,但认为 post 问题和我的解决方案可能很有趣,看看是否有人有一些想法可以摆脱或发现这很有趣。有时似乎很难找到 Pandas 使我从初级到中级熟练程度的信息。
我想这个问题可能有一个更优雅的解决方案,而且可能还有一个更广泛适用的概念,我可以在这里学习和应用以及其他问题。事不宜迟。还是无聊?...
我有一组财务收益数据。它看起来像这样:
Index Symbol Time Earning_Date Year Quarter Last_Quarter
0 AAPL 16:30:00 10/27/2015 2015 Q4 Q3
1 AAPL 16:30:00 7/21/2015 2015 Q3 Q2
2 AAPL 16:30:00 4/27/2015 2015 Q2 Q1
3 AAPL 16:30:00 1/27/2015 2015 Q1 Q4
4 AAPL 16:30:00 10/20/2014 2014 Q4 Q3
5 AAPL 16:30:00 7/22/2014 2014 Q3 Q2
6 AAPL 16:30:00 4/23/2014 2014 Q2 Q1
7 AAPL 16:30:00 1/27/2014 2014 Q1 Q4
8 AAPL 16:30:00 10/28/2013 2013 Q4 Q3
9 AAPL 16:30:00 7/23/2013 2013 Q3 Q2
10 AAPL 16:30:00 4/23/2013 2013 Q2 Q1
11 AAPL 16:30:00 1/23/2013 2013 Q1 Q4
12 AAPL 16:30:00 10/25/2012 2012 Q4 Q3
13 AAPL 16:30:00 7/24/2012 2012 Q3 Q2
14 AAPL 16:30:00 4/24/2012 2012 Q2 Q1
15 AAPL 16:30:00 1/24/2012 2012 Q1 Q4
16 AAPL 16:30:00 10/18/2011 2011 Q4 Q3
17 AAPL 16:30:00 7/19/2011 2011 Q3 Q2
18 AAPL 16:30:00 4/20/2011 2011 Q2 Q1
19 AAPL 16:30:00 1/18/2011 2011 Q1 NaN
首先,完全披露 - 我已经用 'solution' 填充了这个 DF 来解决将 Last_Quarter 附加到每一行的问题 - 我只是使用 .shift(-1) 来填充那个。我确信这可以做得更好——数据对 DF 至关重要,因为它帮助我以两种方式解决了问题。但是如果我们不使用 Last_Quarter 列就可以解决问题。希望这是有道理的。
更大的问题是清理可能丢失或错误的数据。如果一只股票跳过了一个收益季度,或者我的数据有其他损坏,序列可能会显示为 Index 2 Quarter Q2,Index 3 Quarter Q4,因此跳过了 Q3,然后许多假设可能是错误的。所以我只想确保所有数据 Q1 跟随 Q4,Q2 跟随 Q1,Q3 跟随 Q2,Q4 跟随 Q3。
如果数据不好,至少要踢出一个异常。这是我们提出的两个解决方案:
accptbl_qtr_pr_tpls = [('Q3','Q4'),('Q4','Q1'),('Q1','Q2'),('Q2','Q3')]
rows_that_pass = 0
rows_total = len(self.df)
print 'total rows', rows_total
for accptbl_qtr_pr_tpl in accptbl_qtr_pr_tpls:
foo = self.df.ix[(self.df['Last_Quarter'] == accptbl_qtr_pr_tpl[0]) & (self.df['Quarter'] == accptbl_qtr_pr_tpl[1])]
rows_that_pass += len(foo)
if rows_total != 1+rows_that_pass: # the + 1 is to account for NaN in earliest result last_quarter column
print 'quarter issue!, exiting'
我们还想出了:
if not (((self.df['Last_Quarter'] == 'Q1') & (self.df['Quarter'] == 'Q2')).any() and ((self.df['Last_Quarter'] == 'Q2') & (self.df['Quarter'] == 'Q3')).any() \
and ((self.df['Last_Quarter'] == 'Q3') & (self.df['Quarter'] == 'Q4')).any() and ((self.df['Last_Quarter'] == 'Q4') & (self.df['Quarter'] == 'Q1')).any()):
print "bad data"
else:
print 'good data'
我想我会把它扔在这里,看看我们有多聪明,或者我们浪费了多少时间来解决一个已解决的问题
我会根据 Quarter 和 Last_Quarter 中的值的有效组合编写一个函数 return True 或 False,然后通过应用函数 [=创建一个具有有效状态结果的新列 row-wise.
这将使您能够获取 DataFrame 的一部分,其中只有好行或坏行。
该函数看起来像这样:
def check_quarters(row):
# if either Quarter or Last_Quarter is NaN, return False
if (row['Quarter'] != row['Quarter']) or (row['Last_Quarter'] != row['Last_Quarter']):
return False
# check for valid combination when Quarter is Q2 Q3 or Q4
if int(row['Quarter'][1:2]) - 1 == int(row['Last_Quarter'][1:2]):
return True
# check for valid combination when Quarter is Q1
elif int(row['Quarter'][1:2]) == 1 and int(row['Last_Quarter'][1:2]) == 4:
return True
else:
return False
应用函数创建新列:
df['Valid_Quarters'] = df.apply(check_quarters, axis = 1)
现在您可以对 DataFrame 进行切片以仅获取有效行:
df.loc[df['Valid_Quarters'],:]
我认为您可以将收入转换为时间序列,然后重新采样。我下面的 df
是您的示例 DataFrame。
#Drop a couple of row to test
df = df.drop([3,8,10])
#I'm creating a timestamp index, according to Year & Quarter columns. But if they are guaranteed to be conssitent with Earning_Date, you can use that date directly.
df.index = pd.PeriodIndex(df['Year'].astype(str) + df['Quarter'], freq='Q').to_timestamp() #
#Some random data pretending to be earnings
df['Earnings'] = np.random.rand(len(df))
earnings = df['Earnings'].sort_index().resample('QS') #this will fill in NaN for missing quarters
print earnings
这样的结果(数字是随机的。注意 NaN
s):
2011-01-01 0.215123
2011-04-01 0.161175
2011-07-01 0.476889
2011-10-01 0.280691
2012-01-01 0.384339
2012-04-01 0.358041
2012-07-01 0.985589
2012-10-01 0.515073
2013-01-01 0.675246
2013-04-01 NaN
2013-07-01 0.379003
2013-10-01 NaN
2014-01-01 0.625809
2014-04-01 0.572225
2014-07-01 0.547720
2014-10-01 0.651770
2015-01-01 NaN
2015-04-01 0.318578
2015-07-01 0.713037
2015-10-01 0.799639
Freq: QS-JAN, Name: Earnings, dtype: float64
那么您可以Quarter/Quarter收入的变化为
QoQ_Earnings_Chg = earnings.diff()
print QoQ_Earnings_Chg
缺少季度会给你 NaN
QoQ 变化。
2011-01-01 NaN
2011-04-01 -0.053948
2011-07-01 0.315714
2011-10-01 -0.196198
2012-01-01 0.103648
2012-04-01 -0.026298
2012-07-01 0.627548
2012-10-01 -0.470516
2013-01-01 0.160172
2013-04-01 NaN
2013-07-01 NaN
2013-10-01 NaN
2014-01-01 NaN
2014-04-01 -0.053584
2014-07-01 -0.024505
2014-10-01 0.104050
2015-01-01 NaN
2015-04-01 NaN
2015-07-01 0.394458
2015-10-01 0.086602
Freq: QS-JAN, Name: Earnings, dtype: float64
由于重采样,这与原始 df
的长度不同,但您可以加入 df
print df.join(QoQ_Earnings_Chg, rsuffix='_QoQChg')
Symbol Time Earning_Date Year Quarter Last_Quarter Earnings \
2015-10-01 AAPL 16:30:00 10/27/2015 2015 Q4 Q3 0.799639
2015-07-01 AAPL 16:30:00 7/21/2015 2015 Q3 Q2 0.713037
2015-04-01 AAPL 16:30:00 4/27/2015 2015 Q2 Q1 0.318578
2014-10-01 AAPL 16:30:00 10/20/2014 2014 Q4 Q3 0.651770
2014-07-01 AAPL 16:30:00 7/22/2014 2014 Q3 Q2 0.547720
2014-04-01 AAPL 16:30:00 4/23/2014 2014 Q2 Q1 0.572225
2014-01-01 AAPL 16:30:00 1/27/2014 2014 Q1 Q4 0.625809
2013-07-01 AAPL 16:30:00 7/23/2013 2013 Q3 Q2 0.379003
2013-01-01 AAPL 16:30:00 1/23/2013 2013 Q1 Q4 0.675246
2012-10-01 AAPL 16:30:00 10/25/2012 2012 Q4 Q3 0.515073
2012-07-01 AAPL 16:30:00 7/24/2012 2012 Q3 Q2 0.985589
2012-04-01 AAPL 16:30:00 4/24/2012 2012 Q2 Q1 0.358041
2012-01-01 AAPL 16:30:00 1/24/2012 2012 Q1 Q4 0.384339
2011-10-01 AAPL 16:30:00 10/18/2011 2011 Q4 Q3 0.280691
2011-07-01 AAPL 16:30:00 7/19/2011 2011 Q3 Q2 0.476889
2011-04-01 AAPL 16:30:00 4/20/2011 2011 Q2 Q1 0.161175
2011-01-01 AAPL 16:30:00 1/18/2011 2011 Q1 NaN 0.215123
Earnings_QoQChg
2015-10-01 0.086602
2015-07-01 0.394458
2015-04-01 NaN
2014-10-01 0.104050
2014-07-01 -0.024505
2014-04-01 -0.053584
2014-01-01 NaN
2013-07-01 NaN
2013-01-01 0.160172
2012-10-01 -0.470516
2012-07-01 0.627548
2012-04-01 -0.026298
2012-01-01 0.103648
2011-10-01 -0.196198
2011-07-01 0.315714
2011-04-01 -0.053948
2011-01-01 NaN
在对季度收益日期的 Pandas DataFrame 进行操作时,我意识到我想进行季度间(即 Q2 到 Q3)比较,我意识到我应该确定我的数据是订购正确且完整。
所以, 1. 总是 'correct' 季度 (q1->q2->q3->q4->q1->q2...) 2. 并且没有遗漏任何四分之一 (q1->q2->q4->q1->q2->q3) 不如遗漏 q3.
我已经加载了一个数据框,我认为在 pandas 中检查它比返回并检查预加载更有意义。
我想出了我认为是解决问题的两个稍微创可贴的解决方案,但认为 post 问题和我的解决方案可能很有趣,看看是否有人有一些想法可以摆脱或发现这很有趣。有时似乎很难找到 Pandas 使我从初级到中级熟练程度的信息。
我想这个问题可能有一个更优雅的解决方案,而且可能还有一个更广泛适用的概念,我可以在这里学习和应用以及其他问题。事不宜迟。还是无聊?...
我有一组财务收益数据。它看起来像这样:
Index Symbol Time Earning_Date Year Quarter Last_Quarter
0 AAPL 16:30:00 10/27/2015 2015 Q4 Q3
1 AAPL 16:30:00 7/21/2015 2015 Q3 Q2
2 AAPL 16:30:00 4/27/2015 2015 Q2 Q1
3 AAPL 16:30:00 1/27/2015 2015 Q1 Q4
4 AAPL 16:30:00 10/20/2014 2014 Q4 Q3
5 AAPL 16:30:00 7/22/2014 2014 Q3 Q2
6 AAPL 16:30:00 4/23/2014 2014 Q2 Q1
7 AAPL 16:30:00 1/27/2014 2014 Q1 Q4
8 AAPL 16:30:00 10/28/2013 2013 Q4 Q3
9 AAPL 16:30:00 7/23/2013 2013 Q3 Q2
10 AAPL 16:30:00 4/23/2013 2013 Q2 Q1
11 AAPL 16:30:00 1/23/2013 2013 Q1 Q4
12 AAPL 16:30:00 10/25/2012 2012 Q4 Q3
13 AAPL 16:30:00 7/24/2012 2012 Q3 Q2
14 AAPL 16:30:00 4/24/2012 2012 Q2 Q1
15 AAPL 16:30:00 1/24/2012 2012 Q1 Q4
16 AAPL 16:30:00 10/18/2011 2011 Q4 Q3
17 AAPL 16:30:00 7/19/2011 2011 Q3 Q2
18 AAPL 16:30:00 4/20/2011 2011 Q2 Q1
19 AAPL 16:30:00 1/18/2011 2011 Q1 NaN
首先,完全披露 - 我已经用 'solution' 填充了这个 DF 来解决将 Last_Quarter 附加到每一行的问题 - 我只是使用 .shift(-1) 来填充那个。我确信这可以做得更好——数据对 DF 至关重要,因为它帮助我以两种方式解决了问题。但是如果我们不使用 Last_Quarter 列就可以解决问题。希望这是有道理的。
更大的问题是清理可能丢失或错误的数据。如果一只股票跳过了一个收益季度,或者我的数据有其他损坏,序列可能会显示为 Index 2 Quarter Q2,Index 3 Quarter Q4,因此跳过了 Q3,然后许多假设可能是错误的。所以我只想确保所有数据 Q1 跟随 Q4,Q2 跟随 Q1,Q3 跟随 Q2,Q4 跟随 Q3。
如果数据不好,至少要踢出一个异常。这是我们提出的两个解决方案:
accptbl_qtr_pr_tpls = [('Q3','Q4'),('Q4','Q1'),('Q1','Q2'),('Q2','Q3')]
rows_that_pass = 0
rows_total = len(self.df)
print 'total rows', rows_total
for accptbl_qtr_pr_tpl in accptbl_qtr_pr_tpls:
foo = self.df.ix[(self.df['Last_Quarter'] == accptbl_qtr_pr_tpl[0]) & (self.df['Quarter'] == accptbl_qtr_pr_tpl[1])]
rows_that_pass += len(foo)
if rows_total != 1+rows_that_pass: # the + 1 is to account for NaN in earliest result last_quarter column
print 'quarter issue!, exiting'
我们还想出了:
if not (((self.df['Last_Quarter'] == 'Q1') & (self.df['Quarter'] == 'Q2')).any() and ((self.df['Last_Quarter'] == 'Q2') & (self.df['Quarter'] == 'Q3')).any() \
and ((self.df['Last_Quarter'] == 'Q3') & (self.df['Quarter'] == 'Q4')).any() and ((self.df['Last_Quarter'] == 'Q4') & (self.df['Quarter'] == 'Q1')).any()):
print "bad data"
else:
print 'good data'
我想我会把它扔在这里,看看我们有多聪明,或者我们浪费了多少时间来解决一个已解决的问题
我会根据 Quarter 和 Last_Quarter 中的值的有效组合编写一个函数 return True 或 False,然后通过应用函数 [=创建一个具有有效状态结果的新列 row-wise.
这将使您能够获取 DataFrame 的一部分,其中只有好行或坏行。
该函数看起来像这样:
def check_quarters(row):
# if either Quarter or Last_Quarter is NaN, return False
if (row['Quarter'] != row['Quarter']) or (row['Last_Quarter'] != row['Last_Quarter']):
return False
# check for valid combination when Quarter is Q2 Q3 or Q4
if int(row['Quarter'][1:2]) - 1 == int(row['Last_Quarter'][1:2]):
return True
# check for valid combination when Quarter is Q1
elif int(row['Quarter'][1:2]) == 1 and int(row['Last_Quarter'][1:2]) == 4:
return True
else:
return False
应用函数创建新列:
df['Valid_Quarters'] = df.apply(check_quarters, axis = 1)
现在您可以对 DataFrame 进行切片以仅获取有效行:
df.loc[df['Valid_Quarters'],:]
我认为您可以将收入转换为时间序列,然后重新采样。我下面的 df
是您的示例 DataFrame。
#Drop a couple of row to test
df = df.drop([3,8,10])
#I'm creating a timestamp index, according to Year & Quarter columns. But if they are guaranteed to be conssitent with Earning_Date, you can use that date directly.
df.index = pd.PeriodIndex(df['Year'].astype(str) + df['Quarter'], freq='Q').to_timestamp() #
#Some random data pretending to be earnings
df['Earnings'] = np.random.rand(len(df))
earnings = df['Earnings'].sort_index().resample('QS') #this will fill in NaN for missing quarters
print earnings
这样的结果(数字是随机的。注意 NaN
s):
2011-01-01 0.215123
2011-04-01 0.161175
2011-07-01 0.476889
2011-10-01 0.280691
2012-01-01 0.384339
2012-04-01 0.358041
2012-07-01 0.985589
2012-10-01 0.515073
2013-01-01 0.675246
2013-04-01 NaN
2013-07-01 0.379003
2013-10-01 NaN
2014-01-01 0.625809
2014-04-01 0.572225
2014-07-01 0.547720
2014-10-01 0.651770
2015-01-01 NaN
2015-04-01 0.318578
2015-07-01 0.713037
2015-10-01 0.799639
Freq: QS-JAN, Name: Earnings, dtype: float64
那么您可以Quarter/Quarter收入的变化为
QoQ_Earnings_Chg = earnings.diff()
print QoQ_Earnings_Chg
缺少季度会给你 NaN
QoQ 变化。
2011-01-01 NaN
2011-04-01 -0.053948
2011-07-01 0.315714
2011-10-01 -0.196198
2012-01-01 0.103648
2012-04-01 -0.026298
2012-07-01 0.627548
2012-10-01 -0.470516
2013-01-01 0.160172
2013-04-01 NaN
2013-07-01 NaN
2013-10-01 NaN
2014-01-01 NaN
2014-04-01 -0.053584
2014-07-01 -0.024505
2014-10-01 0.104050
2015-01-01 NaN
2015-04-01 NaN
2015-07-01 0.394458
2015-10-01 0.086602
Freq: QS-JAN, Name: Earnings, dtype: float64
由于重采样,这与原始 df
的长度不同,但您可以加入 df
print df.join(QoQ_Earnings_Chg, rsuffix='_QoQChg')
Symbol Time Earning_Date Year Quarter Last_Quarter Earnings \
2015-10-01 AAPL 16:30:00 10/27/2015 2015 Q4 Q3 0.799639
2015-07-01 AAPL 16:30:00 7/21/2015 2015 Q3 Q2 0.713037
2015-04-01 AAPL 16:30:00 4/27/2015 2015 Q2 Q1 0.318578
2014-10-01 AAPL 16:30:00 10/20/2014 2014 Q4 Q3 0.651770
2014-07-01 AAPL 16:30:00 7/22/2014 2014 Q3 Q2 0.547720
2014-04-01 AAPL 16:30:00 4/23/2014 2014 Q2 Q1 0.572225
2014-01-01 AAPL 16:30:00 1/27/2014 2014 Q1 Q4 0.625809
2013-07-01 AAPL 16:30:00 7/23/2013 2013 Q3 Q2 0.379003
2013-01-01 AAPL 16:30:00 1/23/2013 2013 Q1 Q4 0.675246
2012-10-01 AAPL 16:30:00 10/25/2012 2012 Q4 Q3 0.515073
2012-07-01 AAPL 16:30:00 7/24/2012 2012 Q3 Q2 0.985589
2012-04-01 AAPL 16:30:00 4/24/2012 2012 Q2 Q1 0.358041
2012-01-01 AAPL 16:30:00 1/24/2012 2012 Q1 Q4 0.384339
2011-10-01 AAPL 16:30:00 10/18/2011 2011 Q4 Q3 0.280691
2011-07-01 AAPL 16:30:00 7/19/2011 2011 Q3 Q2 0.476889
2011-04-01 AAPL 16:30:00 4/20/2011 2011 Q2 Q1 0.161175
2011-01-01 AAPL 16:30:00 1/18/2011 2011 Q1 NaN 0.215123
Earnings_QoQChg
2015-10-01 0.086602
2015-07-01 0.394458
2015-04-01 NaN
2014-10-01 0.104050
2014-07-01 -0.024505
2014-04-01 -0.053584
2014-01-01 NaN
2013-07-01 NaN
2013-01-01 0.160172
2012-10-01 -0.470516
2012-07-01 0.627548
2012-04-01 -0.026298
2012-01-01 0.103648
2011-10-01 -0.196198
2011-07-01 0.315714
2011-04-01 -0.053948
2011-01-01 NaN