Как сделать хорошие воспроизводимые примеры панд
Потратив приличное количество времени на просмотр тегов r и pandas на SO, у меня сложилось впечатление, что pandas
вопросы с меньшей вероятностью будут содержать воспроизводимые данные. Это то , что R сообщество было очень хорошо о поощрении, и благодаря руководству , как это , новички могут получить некоторую помощь в составлении этих примеров. Люди, которые могут прочитать эти руководства и вернуться с воспроизводимыми данными, часто будут иметь гораздо больше успеха в получении ответов на свои вопросы.
Как мы можем создать хорошие воспроизводимые примеры pandas
вопросов? Можно собрать простые фреймы данных, например:
import pandas as pd
df = pd.DataFrame({'user': ['Bob', 'Jane', 'Alice'],
'income': [40000, 50000, 42000]})
Но для многих примеров наборов данных требуется более сложная структура, например:
datetime
индексы или данные- Множественные категориальные переменные (есть ли эквивалент
expand.grid()
функции R , которая производит все возможные комбинации некоторых заданных переменных?) - Данные MultiIndex или Panel
Для наборов данных, которые сложно смоделировать с помощью нескольких строк кода, существует ли эквивалент R, dput()
который позволяет вам генерировать копируемый и вставляемый код для восстановления вашей структуры данных?
Ответы
Примечание. Идеи здесь довольно общие для Stack Overflow, даже вопросы .
Отказ от ответственности: написать хороший вопрос - ТРУДНО.
Хорошо:
включите небольшой * пример DataFrame в виде исполняемого кода:
In [1]: df = pd.DataFrame([[1, 2], [1, 3], [4, 6]], columns=['A', 'B'])
или сделайте его «копируемым и вставляемым» с помощью
pd.read_clipboard(sep='\s\s+')
, вы можете отформатировать текст для выделения Stack Overflow и использовать 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()
, если не поиграться, чтобы увидеть, сможете ли вы создать небольшой DataFrame, который демонстрирует проблему, с которой вы столкнулись.* Каждое правило имеет исключение, очевидно , один для проблем с производительностью ( в этом случае , безусловно , использовать% timeit и , возможно , % prun ), где вы должны генерировать (рассмотреть возможность использования np.random.seed поэтому у нас есть точно такой же кадр):
df = pd.DataFrame(np.random.randn(100000000, 10))
. Сказать, что "сделай этот код быстрым для меня" не совсем по теме сайта ...запишите желаемый результат (как указано выше)
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 находится в индексе, а не столбец.
покажите, что вы провели некоторое исследование ( поиск в документации , поиск в StackOverflow ), дайте сводку:
В строке документации для суммы просто указано «Вычислить сумму значений группы».
В документации 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, который мы не можем скопировать и вставить (см. выше), это своего рода недовольство отображением панд по умолчанию, но, тем не менее, раздражает:
In [11]: df Out[11]: C A B 1 2 3 2 6
Правильный способ - включить обычный DataFrame с
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 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, который является "большим", упомяните мимоходом некоторые имена столбцов (не забудьте не указывать их типы). Постарайтесь подробно рассказать о чем-то совершенно бессмысленном, не видя реального контекста. Предположительно никто даже не будет дочитать до конца этот абзац.
Эссе - это плохо, проще с небольшими примерами.
не включайте 10+ (100+ ??) строк с данными перед тем, как перейти к вашему актуальному вопросу.
Пожалуйста, мы видим достаточно этого в нашей повседневной работе. Мы хотим помочь, но не так, как это ... .
Вырежьте вступление и просто покажите соответствующие DataFrames (или их небольшие версии) на этапе, который вызывает у вас проблемы.
В любом случае, получайте удовольствие от изучения Python, NumPy и Pandas!
Как создать образцы наборов данных
В основном это делается для того, чтобы расширить ответ @ 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
Некоторые примечания:
np.repeat
иnp.tile
(столбцыd
иe
) очень полезны для регулярного создания групп и индексов. Для двух столбцов это можно использовать для простого дублирования r,expand.grid()
но он также более гибок в плане возможности предоставить подмножество всех перестановок. Однако для 3 или более столбцов синтаксис быстро становится громоздким.- Для более прямой замены r
expand.grid()
см.itertools
Решение в кулинарной книге pandas илиnp.meshgrid
решение, показанное здесь . Это позволит любое количество измерений. - С помощью
np.random.choice
. Например, в столбцеg
у нас есть случайный выбор из 6 дат с 2011 года. Кроме того, установив,replace=False
мы можем гарантировать, что эти даты уникальны - очень удобно, если мы хотим использовать это как индекс с уникальными значениями.
Поддельные данные фондового рынка
Помимо взятия подмножеств приведенного выше кода, вы можете комбинировать методы, чтобы делать что угодно. Например, вот короткий пример, который объединяет np.tile
и date_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
Дневник отвечающего
Мой лучший совет при задании вопросов - играть на психологии людей, отвечающих на вопросы. Как один из таких людей, я могу понять, почему я отвечаю на одни вопросы и почему не отвечаю на другие.
Мотивации
У меня есть мотивация отвечать на вопросы по нескольким причинам
- Stackoverflow.com был для меня чрезвычайно ценным ресурсом. Я хотел отдать.
- В своих усилиях по возвращению я обнаружил, что этот сайт стал еще более мощным ресурсом, чем раньше. Отвечая на вопросы, я учусь, и мне нравится учиться. Прочтите этот ответ и комментарий другого ветеринара . Такое общение меня радует.
- Мне нравятся очки!
- См. № 3.
- Люблю интересные задачи.
Все мои чистейшие намерения велики и все такое, но я получаю удовлетворение, если отвечаю на 1 или 30 вопросов. Что движет моим выбором, на какие вопросы отвечать, имеет огромный компонент максимизации баллов.
Я также трачу время на интересные проблемы, но их немного, и они не помогут тому, кто задает вопрос, которому нужно решение неинтересного вопроса. Лучший способ заставить меня ответить на вопрос - это подать этот вопрос на блюде, чтобы я мог ответить на него с минимальными усилиями. Если я смотрю на два вопроса и у одного есть код, я могу скопировать вставку, чтобы создать все необходимые мне переменные ... Я беру этот! Я вернусь к другому, если у меня будет время, может быть.
Главный совет
Сделайте так, чтобы люди могли легко отвечать на вопросы.
- Предоставьте код, который создает необходимые переменные.
- Сверните этот код. Если мои глаза тускнеют, когда я смотрю на пост, я перехожу к следующему вопросу или возвращаюсь к тому, чем еще занимаюсь.
- Подумайте, о чем вы спрашиваете, и будьте конкретны. Мы хотим увидеть, что вы сделали, потому что естественный язык (английский) неточен и сбивает с толку. Примеры кода того, что вы пробовали, помогают устранить несоответствия в описании на естественном языке.
- ПОЖАЛУЙСТА, покажите, чего вы ожидаете !!! Я должен сесть и попробовать. Я почти никогда не узнаю ответ на вопрос, не попробовав кое-что. Если я не увижу пример того, что вы ищете, я могу пропустить вопрос, потому что не хочу гадать.
Ваша репутация - это больше, чем просто ваша репутация.
Мне нравятся очки (я уже говорил об этом выше). Но на самом деле это не моя репутация. Моя настоящая репутация - это результат того, что думают обо мне другие на сайте. Я стремлюсь быть справедливым и честным и надеюсь, что другие это поймут. Для спрашивающего это означает, что мы помним поведение спрашивающего. Если вы не выбираете ответы и не голосуете за хорошие ответы, я помню. Если вы ведете себя так, как мне не нравится, или как я люблю, я вспоминаю. Это также влияет на то, на какие вопросы я отвечу.
В любом случае, я, вероятно, смогу продолжить, но я пощажу всех, кто действительно это читает.
Задача Один из самых сложных аспектов ответа на вопросы SO - время, необходимое для воссоздания проблемы (включая данные). На вопросы, которые не имеют четкого способа воспроизведения данных, ответ будет меньше. Учитывая, что вы нашли время, чтобы написать вопрос, и у вас есть проблема, с которой вы хотели бы помочь, вы можете легко помочь себе, предоставив данные, которые затем могут использовать другие для решения вашей проблемы.
Инструкции по написанию хороших вопросов Pandas, предоставленные @Andy, - отличное место для начала. Для получения дополнительной информации обратитесь к тому, как задать вопрос и как создать минимальные, полные и проверяемые примеры .
Пожалуйста, четко сформулируйте свой вопрос заранее. Потратив время на то, чтобы написать свой вопрос и любой пример кода, попробуйте прочитать его и предоставить читателю «Краткое изложение», в котором кратко изложена проблема и четко сформулирован вопрос.
Исходный вопрос :
У меня есть эти данные ...
Я хочу сделать это...
Я хочу, чтобы мой результат выглядел так ...
Однако, когда я пытаюсь сделать [это], у меня возникает следующая проблема ...
Я пытался найти решения, делая [то] и [то].
Как мне это исправить?
В зависимости от объема данных, примеров кода и стека ошибок читатель должен пройти долгий путь, прежде чем понять, в чем проблема. Попробуйте переформулировать свой вопрос так, чтобы сам вопрос был вверху, а затем укажите необходимые детали.
Исправленный вопрос :
Вопрос: Как я могу [это] сделать?
Я пытался найти решения, делая [то] и [то].
Когда я попытался сделать [это], у меня возникла следующая проблема ...
Я бы хотел, чтобы мои окончательные результаты выглядели так ...
Вот минимальный код, который может воспроизвести мою проблему ...
А вот как воссоздать мои образцы данных:
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) })
Если это были ваши фактические данные, вы можете просто включить заголовок и / или хвост фрейма данных следующим образом (обязательно анонимизируйте любые конфиденциальные данные):
>>> 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
Вот моя версия 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))
против
{'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.dtypes
есть uint8
и pd.DataFrame(du.to_dict()).dtypes
есть int64
.