如何制作好的可重复的熊猫示例

时间:2013-11-20 23:31:40

标签: python pandas

花了相当多的时间观察SO上的标记,我得到的印象是pandas个问题不太可能包含可重现的数据。这是R社区非常善于鼓励的事情,并且由于像this这样的指南,新人能够得到一些帮助来组合这些例子。能够阅读这些指南并返回可重现数据的人通常可以更好地获得他们问题的答案。

我们如何为pandas问题创建良好的可重复示例?简单的数据帧可以放在一起,例如:

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

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

  • datetime索引或数据
  • 多个分类变量(是否等同于R的expand.grid()函数,它会产生某些给定变量的所有可能组合?)
  • MultiIndex或面板数据

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

5 个答案:

答案 0 :(得分:237)

注意:这里的想法对于StackOverflow来说非常通用,确实是questions

免责声明:写一个好问题很难。

好的:

  • 包含小*示例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]: 
       A  B
    0  1  2
    1  1  3
    2  4  6
    

    自己测试pd.read_clipboard(sep='\s\s+')

    * 我的意思是,绝大多数示例DataFrames可能少于6行需要引用我打赌我可以分5行进行。你可以使用df = df.head()重现错误,如果没有蠢事,看看你是否可以组成一个展示你所面临问题的小型数据框。

    * 每个规则都有一个例外,显而易见的是性能问题(in which case definitely use %timeit and possibly %prun),你应该在哪里生成(考虑使用np.random.seed,所以我们有完全相同的框架):{ {1}}。这样说,“让我快速编写代码”并不是严格意义上的网站主题......

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

    df = pd.DataFrame(np.random.randn(100000000, 10))

    解释数字的来源:5是A为1的行的B列之和。

  • 显示您尝试过的代码

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

    但是说出错误的是:A列在索引而不是列中。

  • 确实展示了你做过一些研究(search the docssearch StackOverflow),总结一下:

      

    sum的docstring只是简单地说明了“组值的计算和”

         

    groupby docs没有给出任何示例。

    除此之外:这里的答案是使用In [4]: df.groupby('A').sum() Out[4]: B A 1 5 4 6

  • 如果您有Timestamp列,例如你正在重新采样或其他什么,然后明确地将df.groupby('A', as_index=False).sum()应用于他们以获得良好的衡量标准**。

    pd.to_datetime

    ** 有时这就是问题本身:它们是字符串。

坏:

  • 不包含MultiIndex,其中我们无法复制和粘贴(见上文),这是对pandas默认显示的一种不满,但仍然令人讨厌:

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

    正确的方法是使用set_index调用包含普通的DataFrame:

    In [11]: df
    Out[11]:
         C
    A B   
    1 2  3
      2  6
    
  • 在提供您想要的结果时提供洞察力:

    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
    A B   
    1 2  3
      2  6
    

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

  • 如果您的代码抛出错误,请包含整个堆栈跟踪(如果它太嘈杂,可以稍后将其删除)。显示行号(以及它所引发的代码的相应行)。

<丑陋:
  • 不要链接到我们无权访问的csv(理想情况下根本不链接到外部源...)

       B
    A   
    1  1
    5  0
    

    大多数数据都是专有的我们得到了:制作类似的数据并查看是否可以重现问题(小事)。

  • 不要用语言模糊地解释这种情况,就像你有一个“大”的DataFrame,提到一些传递的列名(一定不要提及他们的dtypes)。尝试并详细了解一些在没有看到实际情况的情况下完全没有意义的细节。据推测,没有人会在本段末尾阅读。

    散文很糟糕,小例子更容易。

  • 在回答实际问题之前,不要包含10+(100+ ??)行数据。

    请在我们的日常工作中看到足够的这一点。我们想提供帮助,但not like this...
    剪切介绍,只需在导致您遇到麻烦的步骤中显示相关的DataFrame(或其小版本)。

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

答案 1 :(得分:58)

如何创建样本数据集

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

导入numpy和pandas后,如果您希望人们能够准确地重现您的数据和结果,请务必提供随机种子。

import numpy as np
import pandas as pd

np.random.seed(123)

厨房水槽示例

这是一个展示您可以做的各种事情的示例。可以从以下部分创建各种有用的样本数据帧:

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) 
    })

这会产生:

          a   b       c  d  e          f          g
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.tile(列de)对于以非常规的方式创建群组和索引非常有用。对于2列,这可用于轻松复制r expand.grid(),但在提供所有排列的子集方面也更灵活。但是,对于3列或更多列,语法很快变得难以处理。
  2. 要更直接地替换r expand.grid(),请参阅pandas cookbookitertools解决方案here中的np.meshgrid解决方案。这些将允许任意数量的维度。
  3. 您可以使用np.random.choice做很多事情。例如,在g列中,我们随机选择了2011年的6个日期。此外,通过设置replace=False,我们可以确保这些日期是唯一的 - 如果我们想要将其用作具有唯一值的索引。
  4. 假股市数据

    除了获取上述代码的子集之外,您还可以进一步组合这些技术来执行任何操作。例如,这是一个简短的示例,它结合了np.tiledate_range来为包含相同日期的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个日期),但是我们只使用了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
    

答案 2 :(得分:34)

回答者的日记

我提出问题的最佳建议是发挥回答问题的人的心理。作为这些人中的一员,我可以深入了解为什么我回答某些问题以及为什么我不回答其他问题。

动机

我有动力回答问题有几个原因

  1. Stackoverflow.com对我来说是一个非常宝贵的资源。我想回馈。
  2. 在我努力回馈的过程中,我发现这个网站比以前更加强大。回答问题对我来说是一次学习经历,我喜欢学习。 Read this answer and comment from another vet。这种互动让我很开心。
  3. 我喜欢点!
  4. 见#3。
  5. 我喜欢有趣的问题。
  6. 我所有的最纯粹的意图都是伟大的,但是如果我回答1个问题或30个问题,我会感到满意。我的选择是什么驱动哪个问题需要回答哪个问题具有点最大化的重要组成部分。< / p>

    我还会把时间花在有趣的问题上,但这种情况很少,而且对于需要解决一个非有趣问题的提问者没有帮助。让我回答一个问题的最好办法就是把这个问题放在一个成熟的盘子上,让我尽可能少地回答它。如果我正在看两个问题,一个有代码我可以复制粘贴来创建我需要的所有变量......我正在拿那个!如果我有时间,我会回到另一个,也许。

    主要建议

    让人们轻松回答问题。

    • 提供创建所需变量的代码。
    • 尽量减少该代码。当我看到帖子时,如果我的眼睛睁着眼睛,我会接下来的问题或者回到我正在做的其他事情。
    • 想想你的要求和具体内容。我们想看看你做了什么,因为自然语言(英语)是不精确和令人困惑的。您尝试过的代码示例有助于解决自然语言描述中的不一致问题。
    • 请展示您的期望!!!我必须坐下来尝试一下。如果不尝试一些问题,我几乎不会知道问题的答案。如果我没有看到你正在寻找的例子,我可能会传递这个问题,因为我不想猜测。

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

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

    无论如何,我可以继续,但我会饶恕所有真正读过这篇文章的人。

答案 3 :(得分:19)

挑战回答SO问题最具挑战性的方面之一是重新创建问题(包括数据)所需的时间。没有明确方法来重现数据的问题不太可能得到回答。鉴于您正在花时间撰写问题并且您遇到了需要帮助的问题,您可以通过提供其他人可以用来帮助解决问题的数据轻松地帮助自己。

@Andy提供的关于写好Pandas问题的说明是一个很好的起点。有关详情,请参阅how to ask以及如何创建Minimal, Complete, and Verifiable examples

请提前明确说明您的问题。在花时间撰写您的问题和任何示例代码后,请尝试阅读并提供“执行摘要”。为您的读者总结问题并明确说明问题。

原始问题

  

我有这些数据......

     

我想这样做......

     

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

     

但是,当我尝试[此]时,我遇到以下问题......

     

我试图通过[this]和[that]来找到解决方案。

     

我该如何解决?

根据提供的数据量,示例代码和错误堆栈,读者需要花费很长时间才能了解问题所在。尝试重述您的问题,以便问题本身位于最前面,然后提供必要的详细信息。

修订问题

  

Qustion:我该怎么办?

     

我试图通过[this]和[that]来找到解决方案。

     

当我尝试[此]时,我遇到以下问题......

     

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

     

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

     

以下是重建我的示例数据的方法:       df = pd.DataFrame({'A': [...], 'B': [...], ...})

如果需要提供样本数据!!!

有时只需要DataFrame的头部或尾部即可。您还可以使用@JohnE提出的方法来创建可由其他人复制的更大数据集。用他的例子生成一个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的说明(仅使用相关列)。这使其他人可以更轻松地检查每列的数据类型并识别其他常见错误(例如,日期为字符串与datetime64对象):

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

重新创建索引
# 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

答案 4 :(得分:7)

这是我的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

,但保留了列类型。 例如,在上述测试案例中,

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

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