如何使重复pandas的例子

花了相当多的时间在SO上观看r和pandas标签,我得到的印象是pandas问题不太可能包含可再现的数据。 这是R社区对鼓励的一个很好的东西,并且由于这样的指导,新来者能够在这些例子中得到一些帮助。 能够阅读这些指南并返回可复制数据的人通常会有更好的运气来获得他们的问题的答案。

我们怎样才能为pandas问题创造出很好的重现性例子? 简单的数据框可以放在一起,例如:

 import pandas as pd df = pd.DataFrame({'user': ['Bob', 'Jane', 'Alice'], 'income': [40000, 50000, 42000]}) 

但是很多示例数据集都需要更复杂的结构,例如:

  • datetime指数或数据
  • 多个分类variables(是否有与R的expand.grid()函数等价的函数,它会产生一些给定variables的所有可能组合?)
  • MultiIndex或Panel数据

对于dput()几行代码模拟的数据集,是否有与R的dput()等效的dput() ,允许您生成可复制粘贴的代码来重新生成数据结构?

注意:这里的想法对于StackOverflow来说非常通用,实际上是一些问题 。

免责声明:写一个很好的问题是硬。

好:

  • 包括小*示例DataFrame,或者作为可运行代码:

     In [1]: df = pd.DataFrame([[1, 2], [1, 3], [4, 6]], columns=['A', 'B']) 

    或者使用pd.read_clipboard(sep='\s\s+')将其设置为“复制和粘贴”,您可以格式化StackOverflow突出显示的文本,并使用Ctrl + K(或在每行前面加上四个空格):

     In [2]: df Out[2]: AB 0 1 2 1 1 3 2 4 6 

    自己testingpd.read_clipboard(sep='\s\s+')

    * 我确实意味着很小 ,绝大多数示例DataFrame可能less于6行引用需要我打赌我可以做5行。 你是否可以用df = df.head()重现错误,如果不是df = df.head() ,看看你是否可以组成一个小型的DataFrame来展示你所面临的问题。

    * 每条规则都有一个例外,明显的是性能问题( 在这种情况下肯定使用%timeit和%prun ),你应该在那里生成(考虑使用np.random.seed,所以我们有完全相同的框架): df = pd.DataFrame(np.random.randn(100000000, 10)) 说,“让我的代码快”不是严格的网站主题…

  • 写出你想要的结果(与上面类似)

     In [3]: iwantthis Out[3]: AB 0 1 5 1 4 6 

    请解释数字来自哪里:5是A为1的行的B列的总和。

  • 显示你试过的代码

     In [4]: df.groupby('A').sum() Out[4]: B A 1 5 4 6 

    但是说出了什么是不正确的:A列在索引而不是列中。

  • 显示你已经做了一些研究( search文档 , searchStackOverflow ),给出一个总结:

    总和文档string简单地陈述“计算组值的总和”

    groupby文件没有给出任何例子。

    另外:这里的答案是使用df.groupby('A', as_index=False).sum()

  • 如果你有Timestamp列是相关的,例如你正在重新采样或者什么的话,那么就明确一点,把pd.to_datetime应用到他们,以获得好的度量**。

     df['date'] = pd.to_datetime(df['date']) # this column ought to be date.. 

    ** 有时候这就是问题本身:它们是string。

坏:

  • 不包括一个MultiIndex, 我们不能复制和粘贴 (见上面),这是pandas默认显示的一种委屈,但仍令人讨厌:

     In [11]: df Out[11]: C AB 1 2 3 2 6 

    正确的方法是用一个set_index调用包含一个普通的set_index

     In [12]: df = pd.DataFrame([[1, 2, 3], [1, 2, 6]], columns=['A', 'B', 'C']).set_index(['A', 'B']) In [13]: df Out[13]: C AB 1 2 3 2 6 
  • 当提供你想要的结果时,提供洞察力:

      B A 1 1 5 0 

    具体说明你如何得到这些数字(他们是什么)…仔细检查他们是正确的。

  • 如果你的代码抛出一个错误,那么包含整个堆栈跟踪(如果它太嘈杂,可以稍后编辑它)。 显示行号(以及相应的代码行)。

丑陋:

  • 不要链接到我们无法访问的csv(理想情况下根本不要链接到外部源…)

     df = pd.read_csv('my_secret_file.csv') # ideally with lots of parsing options 

    大多数数据是专有的,我们得到的:组成类似的数据,看看你是否可以重现这个问题(小东西)。

  • 不要用含糊的词语来解释这个情况,就像你有一个“大”的DataFrame一样,提及一些在传递中的列名(一定不要提及它们的dtype)。 试着去看看很多没有看到实际情况的完全没有意义的细节。 据推测,没有人甚至会读到本段的末尾。

    散文是不好的,用小的例子会比较容易。

  • 在解决实际问题之前,不要包含10+(100+)个数据线。

    请在我们的日常工作中看到这一点。 我们想帮忙,但不是这样的…。
    剪切介绍,只显示相关的dataframe(或他们的小版本)在导致你麻烦的步骤。

无论如何,玩得开心学python,numpy和pandas!

如何创build示例数据集

这主要是通过提供如何创build示例数据框的示例来扩展@ AndyHayden的答案。 大pandas和(尤其是)numpy为您提供了各种各样的工具,通常只需几行代码即可创build任何真实数据集的合理传真。

导入numpy和pandas之后,如果你想让人们能够精确地复制你的数据和结果,一定要提供一个随机种子。

 import numpy as np import pandas as pd np.random.seed(123) 

厨房水槽的例子

下面是一个例子,展示了你可以做的各种事情。 各种有用的示例数据框可以从这个子集创build:

 df = pd.DataFrame({ # some ways to create random data 'a':np.random.randn(6), 'b':np.random.choice( [5,7,np.nan], 6), 'c':np.random.choice( ['panda','python','shark'], 6), # some ways to create systematic groups for indexing or groupby # this is similar to r's expand.grid(), see note 2 below 'd':np.repeat( range(3), 2 ), 'e':np.tile( range(2), 3 ), # a date range and set of random dates 'f':pd.date_range('1/1/2011', periods=6, freq='D'), 'g':np.random.choice( pd.date_range('1/1/2011', periods=365, freq='D'), 6, replace=False) }) 

这产生:

  abcdefg 0 -1.085631 NaN panda 0 0 2011-01-01 2011-08-12 1 0.997345 7 shark 0 1 2011-01-02 2011-11-10 2 0.282978 5 panda 1 0 2011-01-03 2011-10-30 3 -1.506295 7 python 1 1 2011-01-04 2011-09-07 4 -0.578600 NaN shark 2 0 2011-01-05 2011-02-27 5 1.651437 7 python 2 1 2011-01-06 2011-02-03 

一些说明:

  1. np.repeatnp.tilede列)对于以非常规的方式创build组和索引非常有用。 对于2列,这可以用来轻松复制r的expand.grid()但是也可以更灵活地提供所有排列的子集。 但是,对于3列或更多列,语法很快变得笨拙。
  2. 要更直接地replacer的expand.grid()请参阅pandas cookbook中的itertools解决scheme或此处显示的np.meshgrid解决scheme。 这些将允许任何数量的维度。
  3. 你可以用np.random.choice来做很多np.random.choice 。 例如,在g列中,我们从2011年开始随机select6个date。另外,通过设置replace=False我们可以确保这些date是唯一的 – 如果我们希望将其用作具有唯一值的索引,则非常方便。

假股市数据

除了上述代码的子集之外,您还可以将这些技术进一步结合起来做任何事情。 例如,下面是一个简短的例子,它结合了np.tiledate_range来创build涵盖相同date的4只股票的样本股票数据:

 stocks = pd.DataFrame({ 'ticker':np.repeat( ['aapl','goog','yhoo','msft'], 25 ), 'date':np.tile( pd.date_range('1/1/2011', periods=25, freq='D'), 4 ), 'price':(np.random.randn(100).cumsum() + 10) }) 

现在我们有一个包含100行(每个行情25个date)的示例数据集,但是我们只用了4行来完成,使得其他人无需复制和粘贴100行代码就可以轻松地进行复制。 然后,您可以显示数据的子集,如果它有助于解释您的问题:

 >>> stocks.head(5) date price ticker 0 2011-01-01 9.497412 aapl 1 2011-01-02 10.261908 aapl 2 2011-01-03 9.438538 aapl 3 2011-01-04 9.515958 aapl 4 2011-01-05 7.554070 aapl >>> stocks.groupby('ticker').head(2) date price ticker 0 2011-01-01 9.497412 aapl 1 2011-01-02 10.261908 aapl 25 2011-01-01 8.277772 goog 26 2011-01-02 7.714916 goog 50 2011-01-01 5.613023 yhoo 51 2011-01-02 6.397686 yhoo 75 2011-01-01 11.736584 msft 76 2011-01-02 11.944519 msft 

答复日记

我提出问题的最好build议是发挥回答问题的人的心理。 作为这些人之一,我可以深入了解为什么我回答某些问题,为什么我不回答他人。

动机

出于几个原因,我积极回答问题

  1. Stackoverflow.com一直是一个巨大的宝贵资源给我。 我想回报。
  2. 在我回馈的努力中,我发现这个网站是一个比以前更强大的资源。 回答问题对我来说是一种学习体验,我喜欢学习。 阅读这个答案,并从另一位兽医评论 。 这种互动让我开心。
  3. 我喜欢分!
  4. 见#3。
  5. 我喜欢有趣的问题。

我所有的最纯粹的意图都是伟大的,但是如果我回答1个问题或30个问题,我会得到满意的结果。

我也会把时间花在有趣的问题上,但是这个问题很less,而且也不能帮助那些需要解决一个非趣味问题的提问者。 让我回答一个问题的最好办法就是把这个问题放在一个成熟的盘子上,以尽可能小的努力来回答。 如果我正在看两个问题,一个有代码,我可以复制粘贴来创build所有我需要的variables…我正在拿那个! 如果我有时间的话,我会回到另外一个。

主要build议

方便人们回答问题。

  • 提供创build所需variables的代码。
  • 最小化该代码。 如果在看post的时候,我的眼睛瞪大了眼睛,我就会回到下一个问题,或者回到我正在做的任何事情上。
  • 想想你的要求和具体。 我们想看看你做了什么,因为自然语言(英语)是不精确的,令人困惑。 您尝试过的代码示例有助于解决自然语言描述中的不一致问题。
  • 请显示你的期望! 我必须坐下来试一下 我几乎从来不知道一个问题的答案,没有尝试一些事情。 如果我没有看到你正在寻找的例子,我可能会传递这个问题,因为我不喜欢猜测。

您的声誉不仅仅是您的声誉。

我喜欢点(我在上面提到)。 但是这些点并不是我的名声。 我真正的声誉是网站上的其他人认为我的合并。 我力求公平和诚实,希望别人能看到这一点。 对提问者来说意味着什么,我们记得提问者的行为。 如果你没有select答案,并提出好的答案,我记得。 如果你的行为方式我不喜欢,或者我喜欢,我记得。 这也涉及到我将回答的问题。


无论如何,我可以继续下去,但是我会放弃所有真正阅读这个的人。

挑战回答SO问题最具挑战性的方面之一是重新创build问题所需的时间(包括数据)。 没有清楚的方法来重现数据的问题不太可能得到回答。 考虑到你花时间写一个问题,并且你有一个你想要帮助的问题,你可以通过提供其他人可以用来帮助你解决问题的数据来轻松地帮助你自己。

@Andy提供的写pandas问题的说明是一个很好的开始。 有关更多信息,请参阅如何询问以及如何创build最小,完整和可validation示例 。

请提前说明你的问题。 在花时间写下你的问题和任何示例代码后,尝试阅读并提供给读者的“执行摘要”,其中总结了问题并清楚地说明了问题。

原始问题

我有这个数据…

我想这样做…

我希望我的结果看起来像这样

但是,当我尝试做这个,我得到以下问题…

我试图通过做[这]和[那]find解决scheme。

我该如何解决?

根据所提供的数据量,示例代码和错误堆栈,读者在理解问题之前需要很长的路要走。 尝试重述你的问题,使问题本身在最上面,然后提供必要的细节。

修订问题

枯竭:我该怎么做[这个]?

我试图通过做[这]和[那]find解决scheme。

当我试图做这个,我得到以下问题…

我希望我的最终结果看起来像这样

这里是一些最小的代码,可以重现我的问题…

这里是如何重新创build我的示例数据: df = pd.DataFrame({'A': [...], 'B': [...], ...})

提供样本数据,如果需要的话!

有时只需要DataFrame的头部或尾部即可。 您也可以使用@JohnE提出的方法来创build更大的数据集,以供其他人使用。 用他的例子来生成一个100行的股票价格数据框:

 stocks = pd.DataFrame({ 'ticker':np.repeat( ['aapl','goog','yhoo','msft'], 25 ), 'date':np.tile( pd.date_range('1/1/2011', periods=25, freq='D'), 4 ), 'price':(np.random.randn(100).cumsum() + 10) }) 

如果这是您的实际数据,您可能只想包含数据框的头部和/或尾部(请务必匿名敏感数据):

 >>> stocks.head(5).to_dict() {'date': {0: Timestamp('2011-01-01 00:00:00'), 1: Timestamp('2011-01-01 00:00:00'), 2: Timestamp('2011-01-01 00:00:00'), 3: Timestamp('2011-01-01 00:00:00'), 4: Timestamp('2011-01-02 00:00:00')}, 'price': {0: 10.284260107718254, 1: 11.930300761831457, 2: 10.93741046217319, 3: 10.884574289565609, 4: 11.78005850418319}, 'ticker': {0: 'aapl', 1: 'aapl', 2: 'aapl', 3: 'aapl', 4: 'aapl'}} >>> pd.concat([stocks.head(), stocks.tail()], ignore_index=True).to_dict() {'date': {0: Timestamp('2011-01-01 00:00:00'), 1: Timestamp('2011-01-01 00:00:00'), 2: Timestamp('2011-01-01 00:00:00'), 3: Timestamp('2011-01-01 00:00:00'), 4: Timestamp('2011-01-02 00:00:00'), 5: Timestamp('2011-01-24 00:00:00'), 6: Timestamp('2011-01-25 00:00:00'), 7: Timestamp('2011-01-25 00:00:00'), 8: Timestamp('2011-01-25 00:00:00'), 9: Timestamp('2011-01-25 00:00:00')}, 'price': {0: 10.284260107718254, 1: 11.930300761831457, 2: 10.93741046217319, 3: 10.884574289565609, 4: 11.78005850418319, 5: 10.017209045035006, 6: 10.57090128181566, 7: 11.442792747870204, 8: 11.592953372130493, 9: 12.864146419530938}, 'ticker': {0: 'aapl', 1: 'aapl', 2: 'aapl', 3: 'aapl', 4: 'aapl', 5: 'msft', 6: 'msft', 7: 'msft', 8: 'msft', 9: 'msft'}} 

您可能还想提供DataFrame的描述(仅使用相关列)。 这使得其他人更容易检查每列的数据types,并识别其他常见错误(例如date为string与date时间64与对象):

 stocks.info() <class 'pandas.core.frame.DataFrame'> Int64Index: 100 entries, 0 to 99 Data columns (total 3 columns): date 100 non-null datetime64[ns] price 100 non-null float64 ticker 100 non-null object dtypes: datetime64[ns](1), float64(1), object(1) 

注意:如果您的DataFrame有一个MultiIndex:

如果您的DataFrame具有多索引,则必须在调用to_dict之前先进行重置。 然后您需要使用set_index重新创build索引:

 # MultiIndex example. First create a MultiIndex DataFrame. df = stocks.set_index(['date', 'ticker']) >>> df price date ticker 2011-01-01 aapl 10.284260 aapl 11.930301 aapl 10.937410 aapl 10.884574 2011-01-02 aapl 11.780059 ... # After resetting the index and passing the DataFrame to `to_dict`, make sure to use # `set_index` to restore the original MultiIndex. This DataFrame can then be restored. d = df.reset_index().to_dict() df_new = pd.DataFrame(d).set_index(['date', 'ticker']) >>> df_new.head() price date ticker 2011-01-01 aapl 10.284260 aapl 11.930301 aapl 10.937410 aapl 10.884574 2011-01-02 aapl 11.780059 

这是我的dput版本 – 用于生成可复制报告的标准R工具 – 用于Pandas DataFrame 。 它可能会失败更复杂的框架,但它似乎在简单的情况下工作:

 import pandas as pd def dput (x): if isinstance(x,pd.Series): return "pd.Series(%s,dtype='%s',index=pd.%s)" % (list(x),x.dtype,x.index) if isinstance(x,pd.DataFrame): return "pd.DataFrame({" + ", ".join([ "'%s': %s" % (c,dput(x[c])) for c in x.columns]) + ( "}, index=pd.%s)" % (x.index)) raise NotImplementedError("dput",type(x),x) 

现在,

 df = pd.DataFrame({'a':[1,2,3,4,2,1,3,1]}) assert df.equals(eval(dput(df))) du = pd.get_dummies(df.a,"foo") assert du.equals(eval(dput(du))) di = df di.index = list('abcdefgh') assert di.equals(eval(dput(di))) 

请注意 ,这产生比DataFrame.to_dict更详细的输出,例如,

pd.DataFrame({'foo_1':pd.Series([1,0,0,0,0,1,0,1],dtype ='uint8',index = pd.RangeIndex(start = 0,stop = 8) ,step = 1)),'foo_2':pd.Series([0,1,0,0,1,0,0,0],dtype ='uint8',index = pd.RangeIndex(start = 0,stop = 8,step = 1)),'foo_3':pd.Series([0,0,1,0,0,0,1,0],dtype ='uint8',index = pd.RangeIndex(start = 0 ,stop = 8,step = 1)),'foo_4':pd.Series([0,0,0,1,0,0,0,0],dtype ='uint8',index = pd.RangeIndex(start = 0,stop = 8,step = 1)),index = pd.RangeIndex(start = 0,stop = 8,step = 1))

VS

{'foo_1':{0:1,1:0,2:0,3:0,4:0,5:1,6:0,7:1},'foo_2':{0:0,1: 1:2:0,3:0,4:1,5:0,6:0,7:0},'foo_3':{0:0,1:0,2:1,3:0,4: 0,5:0,6:1,7:0},'foo_4':{0:0,1:0,2:0,3:1,4:0,5:0,6:0,7: 0}}

du以上,但它保留列types。 例如,在上面的testing案例中,

 du.equals(pd.DataFrame(du.to_dict())) ==> False 

因为du.dtypesuint8pd.DataFrame(du.to_dict()).dtypesint64