Как сделать хорошие воспроизводимые примеры панд
Потратив приличное количество времени на просмотр тегов r и pandas на SO, создается впечатление, что pandas
вопросы с меньшей вероятностью содержат воспроизводимые данные. Это то, что сообщество R очень хорошо поощряет, и благодаря подобным руководствам новички могут получить некоторую помощь при составлении этих примеров. Людям, которые могут прочитать эти руководства и вернуться с воспроизводимыми данными, часто будет намного лучше, если они получат ответы на свои вопросы.
Как мы можем создать хорошие воспроизводимые примеры для pandas
вопросы? Простые кадры данных могут быть собраны вместе, например:
import pandas as pd
df = pd.DataFrame({'user': ['Bob', 'Jane', 'Alice'],
'income': [40000, 50000, 42000]})
Но многие примерные наборы данных нуждаются в более сложной структуре, например:
datetime
индексы или данные- Несколько категориальных переменных (есть ли эквивалент R
expand.grid()
функция, которая производит все возможные комбинации некоторых заданных переменных?) - MultiIndex или Panel данные
Для наборов данных, которые трудно смоделировать с использованием нескольких строк кода, есть ли эквивалент R dput()
что позволяет генерировать копируемый код для восстановления структуры данных?
5 ответов
Примечание: идеи здесь довольно общие для Stackru, действительно вопросы.
Отказ от ответственности: написать хороший вопрос трудно.
Хорошо:
включите небольшой * пример DataFrame, либо в качестве исполняемого кода:
In [1]: df = pd.DataFrame([[1, 2], [1, 3], [4, 6]], columns=['A', 'B'])
или сделать его "копировать и вставлять", используя
pd.read_clipboard(sep='\s\s+')
Вы можете отформатировать текст для выделения Stackru и использовать 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 находится в индексе, а не в столбце.
показать, что вы провели какое-то исследование ( поиск в документах, поиск в Stackru), дать резюме:
Строка документа для суммы просто заявляет "Вычислить сумму значений группы"
Групповые документы не дают никаких примеров для этого.
В сторону: ответ здесь заключается в использовании
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, который "большой", упомяните некоторые имена столбцов попутно (не забудьте упомянуть их dtypes). Постарайтесь углубиться в детали о чем-то совершенно бессмысленном, не видя реального контекста. Предположительно никто даже не собирается читать до конца этого параграфа.
Очерки плохие, легче с небольшими примерами.
не включайте более 10 (более 100) строк данных, прежде чем перейти к актуальному вопросу.
Пожалуйста, мы видим достаточно этого в нашей повседневной работе. Мы хотим помочь, но не так...
Вырежьте вступление и просто покажите соответствующие фреймы данных (или их небольшие версии) на шаге, который вызывает у вас проблемы.
В любом случае, получайте удовольствие от изучения питона, пупка и панды!
Как создать образцы наборов данных
Это главным образом для того, чтобы расширить ответ @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
) очень полезны для регулярного создания групп и индексов. Для 2 столбцов это может быть использовано для простого дублированияexpand.grid()
но также более гибок в способности обеспечить подмножество всех перестановок. Однако для 3 или более столбцов синтаксис быстро становится громоздким.- Для более прямой замены R
expand.grid()
увидетьitertools
решение в поваренной книге панд или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
Дневник Отвечающего
Мой лучший совет для того, чтобы задавать вопросы, - играть на психологии людей, которые отвечают на вопросы. Будучи одним из тех людей, я могу понять, почему я отвечаю на некоторые вопросы и почему я не отвечаю на другие.
Мотивы
Я мотивирован отвечать на вопросы по нескольким причинам
- Stackru.com был чрезвычайно ценным ресурсом для меня. Я хотел вернуть.
- В своих попытках вернуть назад я обнаружил, что этот сайт стал еще более мощным ресурсом, чем раньше. Ответы на вопросы - это опыт обучения для меня, и я люблю учиться. Прочитайте этот ответ и комментарий от другого ветеринара. Такое взаимодействие делает меня счастливым.
- Мне нравятся очки!
- Смотрите № 3.
- Мне нравятся интересные проблемы.
Все мои самые чистые намерения велики и все, но я получаю это удовлетворение, если отвечаю на 1 вопрос или на 30. То, что определяет мой выбор, на какие вопросы отвечать, имеет огромный компонент максимизации баллов.
Я также буду тратить время на интересные проблемы, но это мало и далеко друг от друга, и это не помогает задающему, который нуждается в решении неинтересного вопроса. Лучше всего заставить меня ответить на вопрос, чтобы подать этот вопрос на блюдо, готовое для меня, чтобы ответить на него с минимальными усилиями, насколько это возможно. Если я смотрю на два вопроса, и у одного есть код, я могу скопировать вставить, чтобы создать все переменные, которые мне нужны... Я беру этот! Я вернусь к другому, если у меня будет время, может быть.
Главный совет
Облегчите людям, отвечающим на вопросы.
- Предоставьте код, который создает переменные, которые необходимы.
- Минимизируйте этот код. Если мои глаза затуманиваются, когда я смотрю на сообщение, я перехожу к следующему вопросу или возвращаюсь ко всему, что я делаю.
- Подумайте о том, что вы спрашиваете, и будьте конкретны. Мы хотим увидеть, что вы сделали, потому что естественные языки (английский) неточны и сбивают с толку. Примеры кода того, что вы пробовали, помогают устранить несоответствия в описании на естественном языке.
- ПОЖАЛУЙСТА, покажи, что ты ожидаешь!!! Я должен сесть и попробовать вещи. Я почти никогда не знаю ответа на вопрос, не пробуя что-то. Если я не вижу примера того, что вы ищете, я мог бы передать вопрос, потому что мне не хочется гадать.
Ваша репутация - это больше, чем ваша репутация.
Мне нравятся очки (я упоминал об этом выше). Но эти моменты на самом деле не моя репутация. Моя настоящая репутация - это объединение мнений других людей на сайте. Я стараюсь быть честным и честным и надеюсь, что другие это увидят. Что это означает для аскера, мы помним поведение аскеров. Если вы не выбираете ответы и не даете хороших ответов, я помню. Если ты ведешь себя так, как мне не нравится, или так, как мне нравится, я помню. Это также играет на какие вопросы я отвечу.
В любом случае, я, вероятно, могу продолжать, но я пощажу всех вас, кто действительно читает это.
Задача Одним из наиболее сложных аспектов ответа на вопросы SO является время, необходимое для воссоздания проблемы (включая данные). На вопросы, которые не имеют четкого способа воспроизвести данные, менее вероятно, что на них будут даны ответы. Учитывая, что у вас есть время, чтобы написать вопрос, и у вас есть проблема, с которой вы хотели бы помочь, вы можете легко помочь себе, предоставив данные, которые другие затем могут использовать для решения вашей проблемы.
Инструкции @Andy по написанию хороших вопросов для Pandas - отличное место для начала. Для получения дополнительной информации обратитесь к тому, как спрашивать и как создавать минимальные, полные и проверяемые примеры.
Пожалуйста, четко сформулируйте свой вопрос заранее. Потратив время на написание вашего вопроса и любого примера кода, попробуйте прочитать его и предоставьте "Резюме" для вашего читателя, в котором кратко изложена проблема и четко сформулирован вопрос.
Оригинальный вопрос:
У меня есть эти данные...
Я хочу сделать это...
Я хочу, чтобы мой результат выглядел следующим образом...
Однако, когда я пытаюсь сделать [это], я получаю следующую проблему...
Я пытался найти решения, делая [это] и [это].
Как мне это исправить?
В зависимости от объема данных, примера кода и стеков ошибок, читатель должен пройти долгий путь, прежде чем понять, в чем проблема. Попробуйте переформулировать свой вопрос так, чтобы сам вопрос был на вершине, а затем предоставьте необходимые детали.
Пересмотренный вопрос:
Вопрос: Как я могу сделать это?
Я пытался найти решения, делая [это] и [это].
Когда я попытался сделать [это], я получил следующую проблему...
Я бы хотел, чтобы мои окончательные результаты выглядели так...
Вот некоторый минимальный код, который может воспроизвести мою проблему...
А вот как воссоздать мои образцы данных:
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
s. Вероятно, он потерпит неудачу для более сложных кадров, но, похоже, справится с этой задачей в простых случаях:
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, стоп =8, шаг =1))}, индекс =pd.RangeIndex(начало = 0, стоп =8, шаг = 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
,