如何制作好可复制的Pandas的例子

eagi6jfj  于 2023-01-28  发布在  其他
关注(0)|答案(5)|浏览(85)

此问题的答案为community effort。编辑现有答案以改进此帖子。当前不接受新答案或互动。

在花了相当多的时间看了SO上的rpandas标签之后,我得到的印象是pandas问题不太可能包含可重复的数据,这是R社区一直非常鼓励的事情,多亏了this这样的指南,新手能够在整理这些例子方面得到一些帮助。2那些能够阅读这些指南并带着可复制的数据回来的人往往会有更好的运气得到他们问题的答案。
我们如何为pandas问题创建良好的可重复示例?可以将简单的 Dataframe 放在一起,例如:

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

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

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

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

q3qa4bjr

q3qa4bjr1#

  • 注意:这里的大多数想法都是非常通用的堆栈溢出,实际上是一般的问题。另请参见Short, Self Contained, Correct Example。*
    • 免责声明:写出一个好问题是很难的。

好:

  • 一定要包含一个小的示例DataFrame,或者作为可运行的代码:
In [1]: df = pd.DataFrame([[1, 2], [1, 3], [4, 6]], columns=['A', 'B'])

或者使用pd.read_clipboard(sep='\s\s+')使其"可复制和粘贴"。您可以通过突出显示并使用Ctrl + K(或者在每行前添加四个空格)来设置堆栈溢出的文本格式,或者在代码的上方和下方放置三个反引号(′ ′ ′),并且代码不缩进:

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

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

  • 我的意思是。绝大多数示例数据框可能少于6行,[引文需要]和我打赌我可以做到5行。你能用df = df.head()重现这个错误吗?如果不能,看看你能不能弥补一个小的数据框,展示你所面临的问题。

但是每个规则都有一个例外,最明显的例外是性能问题(在这种情况下,一定要使用%timeit,也可能使用%prun),在这里您应该生成:

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

考虑使用np.random.seed,这样我们就有了完全相同的框架。话虽如此,"让这段代码对我来说更快"并不是严格意义上的网站主题。

  • 写下你想要的结果(同上)
In [3]: iwantthis
Out[3]:
   A  B
0  1  5
1  4  6

解释这些数字的来源:
5是B列中A为1的行的总和。

  • 请务必显示您尝试过的 * 代码 *:
In [4]: df.groupby('A').sum()
Out[4]:
   B
A
1  5
4  6

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

sum的文档字符串仅声明"计算组值之和"
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.

有时这就是问题本身:它们是线。

坏:

  • 不要包含MultiIndex,因为它我们无法复制和粘贴(见上文)。这是对Pandas默认显示的一种不满,但仍然很烦人:
In [11]: df
Out[11]:
     C
A B
1 2  3
  2  6

正确的方法是在set_index调用中包含一个普通DataFrame:

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

In [13]: df = df.set_index(['A', 'B'])

In [14]: df
Out[14]:
     C
A B
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+??)行的数据。

拜托,我们在日常工作中已经看到足够多的这种情况了。我们想帮助你,但是not like this...。删除介绍,只显示给你带来麻烦的步骤中的相关 Dataframe (或它们的小版本)。

kulphzqa

kulphzqa2#

如何创建示例数据集

这主要是为了扩展AndyHayden的答案,提供了如何创建样本 Dataframe 的示例。Pandas和(特别是)NumPy为您提供了各种各样的工具,这样您通常可以用几行代码创建任何真实数据集的合理复制品。
导入NumPy和Pandas之后,如果您希望用户能够准确地重现您的数据和结果,请确保提供随机种子。

import numpy as np
import pandas as pd

np.random.seed(123)

厨房Flume示例

下面的例子展示了你可以做的各种事情。所有有用的样本 Dataframe 都可以从它的子集中创建:

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列或更多列,语法很快就会变得笨拙。
    1.对于R的expand.grid()的更直接的替换,参见Pandas食谱中的itertools解或here所示的np.meshgrid解,这些解允许任意维数。
    1.使用np.random.choice可以做很多事情。例如,在g列中,我们随机选择了2011年的6个日期。此外,通过设置replace=False,我们可以确保这些日期是唯一的--如果我们想将其用作具有唯一值的索引,这非常方便。

伪造股市数据

除了上述代码的子集之外,您还可以进一步组合这些技术来完成任何事情。例如,下面是一个简短的示例,它组合了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
qlckcl4x

qlckcl4x3#

回答者的日记
我对提问的最佳建议是利用回答问题的人的心理,作为其中一员,我可以给予他们了解我为什么回答某些问题,为什么不回答其他问题。

动机

我回答问题的动机有以下几个原因

  1. Stackoverflow.com 对我来说是一个非常宝贵的资源,我想给予社会。
    1.在我努力给予社会的过程中,我发现这个网站是一个比以前更强大的资源。回答问题对我来说是一种学习的经历,我喜欢学习。Read this answer and comment from another vet。这种互动让我很快乐。
    1.我喜欢积分!
    1.参见#3。
    1.我喜欢有趣的问题。
    我所有最纯粹的意图都是伟大的,但如果我回答了1个或30个问题,我就会得到满足。是什么驱使我做出选择回答哪些问题,这是得分最大化的一个重要组成部分。
    我也会花时间在有趣的问题上,但这是很少的,并没有帮助一个提问者谁需要一个解决方案,一个不感兴趣的问题。你最好的赌注,让我回答一个问题是提供一个成熟的盘子上的问题,让我回答它尽可能少的努力。如果我正在寻找两个问题,一个有代码,我可以复制粘贴创建所有的变量,我需要...我要那个!如果我有时间,我会再来看另一个的。

主要建议

让回答问题的人容易些。

  • 提供创建所需变量的代码。
  • 最小化代码。如果我看帖子的时候眼神呆滞,我会继续下一个问题,或者回到我正在做的任何事情上。
  • 考虑一下你的问题并具体说明。我们希望看到你所做的工作,因为自然语言(英语)不准确且容易混淆。你所尝试的代码示例有助于解决自然语言描述中的不一致问题。
  • 请展示你所期望的!!!我必须坐下来尝试一些东西。如果不尝试一些东西,我几乎永远不会知道问题的答案。如果我没有看到你所寻找的例子,我可能会传递这个问题,因为我不想猜测。

你的名声不仅仅是你的名声。

我喜欢积分(我在上面提到过)。但这些分数并不是我真正的声誉。我真正的声誉是网站上其他人对我的看法的综合。我努力做到公平和诚实,我希望其他人能看到这一点。这对提问者来说意味着,我们记住了提问者的行为。如果你没有选择答案,也没有给好答案投赞成票,我记得。如果你的行为方式是我不喜欢的,或者是我喜欢的,我都会记得。这也影响到我会回答哪些问题。
无论如何,我可能会继续下去,但我会节省你们所有人谁真正读到这一点。

s8vozzvw

s8vozzvw4#

    • 挑战**回答SO问题最具挑战性的方面之一是重现问题所需的时间(包括数据)。没有清晰的方式重现数据的问题不太可能得到回答。鉴于您正在花时间编写问题,并且您有一个问题需要帮助,您可以通过提供其他人随后可以用来帮助解决您的问题的数据来轻松地帮助自己。

@Andy提供的编写好Pandas问题的说明是一个极好的开始,要了解更多信息,请参阅how to ask和如何创建Minimal, Complete, and Verifiable examples

    • 请提前清楚地陈述您的问题。**在花时间编写问题和任何示例代码之后,请尝试阅读它,并为读者提供一个"执行摘要",其中总结了问题并清楚地陈述了问题。
  • 原问题 *:

我有这些数据...
我想这么做...
我希望我的结果看起来像这样...
但是,当我试图这样做[这]时,我得到了下面的问题...
我试着通过做[这个]和[那个]来找到解决办法。
我该怎么补救?
根据所提供的数据量、示例代码和错误堆栈,读者需要花很长时间才能理解问题所在。尝试重新陈述问题,使问题本身位于顶部,然后提供必要的详细信息。

  • 修改后的问题 *:
    • 问题:**我该怎么做?

我试着通过做[这个]和[那个]来找到解决办法。
当我试着这样做时,我遇到了以下问题......
我希望我的最终结果看起来像这样...
这里有一些最小的代码,可以重现我的问题...
下面是如何重新创建示例数据:df = pd.DataFrame({'A': [...], 'B': [...], ...})

    • 如有需要,请提供样本数据!!!**

有时只需要DataFrame的头部或尾部,你也可以使用@JohnE提出的方法来创建更大的数据集,这样其他人就可以复制这些数据集,使用他的例子来生成一个100行的股票价格DataFrame:

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

如果这是您的实际数据,您可能只想包含 Dataframe 的头部和/或尾部,如下所示(确保匿名化任何敏感数据):

>>> 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、日期为object):

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具有多索引,则必须在调用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
vc6uscn9

vc6uscn95#

下面是我为Pandas DataFrame s编写的dput版本--生成可重复报告的标准R工具。对于更复杂的帧,它可能会失败,但在简单的情况下,它似乎可以完成这项工作:

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

对比

{'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.dtypesuint8并且pd.DataFrame(du.to_dict()).dtypesint64

相关问题