Как разделить код на несколько файлов python

Обновлено: 06.07.2024

При работе с непрерывными числовыми данными часто бывает полезно разделить (to bin) данные на несколько сегментов для дальнейшего анализа. Существует несколько терминов: сегментирование ( bucketing ), дискретное разделение ( discrete binning ), дискретизация ( discretization ) или квантование ( quantization ). Pandas поддерживает эти подходы с помощью функций cut и qcut .

В этой статье говорится о том, как использовать функции pandas для преобразования непрерывных данных в набор дискретных сегментов. Как и многие функции pandas, cut и qcut могут показаться простыми, но у них есть множество возможностей. Думаю, даже опытные пользователи научатся нескольким приемам, которые будут полезны для анализа.

Один из наиболее распространенных случаев биннинга выполняется при создании гистограммы.

Рассмотрим пример с продажами. Гистограмма данных о продажах показывает, как непрерывный набор показателей продаж можно разделить на дискретные ячейки (например: 60 000 – 70 000 долларов США), а затем использовать их для группировки и подсчета учетных записей ( account number ).

Далее представлен код, который показывает, как суммировать информацию о продажах за 2018 год для группы клиентов. Это представление отображает количество клиентов, у которых продажи находятся в определенных диапазонах:

account number name ext price
0 141962 Herman LLC 63626.03
1 146832 Kiehn-Spinka 99608.77
2 163416 Purdy-Kunde 77898.21
3 218895 Kulas Inc 137351.96
4 239344 Stokes LLC 91535.92

Существует множество других сценариев, в которых вы можете определить собственные интервалы (bins).

В приведенном выше примере 8 интервалов с данными. Что, если бы мы захотели разделить наших клиентов на 3 , 4 или 5 групп?

Вот где в игру вступают qcut и cut . Эти функции кажутся похожими и выполняют аналогичные функции группирования, но имеют различия, которые могут сбивать с толку новых пользователей.

Остальная часть статьи покажет, в чем их различия и как их использовать.

В документации qcut описывается как "функция дискретизации на основе квантилей". По сути, это означает, что qcut пытается разделить базовые данные на интервалы равного размера. Функция определяет интервалы с использованием процентилей на основе распределения данных, а не фактических числовых границ интервалов.

Если вы ранее использовали функцию description , то уже встречали пример основных концепций, представленных qcut :

Запомните значения для 25% , 50% и 75% процентилей, поскольку мы напрямую рассматрим использование qcut .

Самое простое использование qcut - определить количество квантилей и позволить pandas разделить данные.

В приведенном ниже примере мы просим pandas создать 4 группы одинакового размера:

В результате получается категориальный ряд (про категориальный тип данных в pandas см. тут), представляющий интервалы с продажами. Поскольку мы запросили квантили с q=4 , поэтому интервалы соответствуют процентилям из функции describe .

Типичным вариантом использования является сохранение результатов разбиения в исходном фрейме данных ( dataframe ) для будущего анализа.

В следующем примере мы создадим 4 интервала (также называемых квартилями) и 10 интервалов (также называемых децилями) и сохраним результаты обратно в исходный фрейм данных:

account number name ext price quantile_ex_1 quantile_ex_2
0 141962 Herman LLC 63626.03 (55733.049000000006, 89137.708] (55732.0, 76471.0]
1 146832 Kiehn-Spinka 99608.77 (89137.708, 100271.535] (95908.0, 100272.0]
2 163416 Purdy-Kunde 77898.21 (55733.049000000006, 89137.708] (76471.0, 87168.0]
3 218895 Kulas Inc 137351.96 (110132.552, 184793.7] (124778.0, 184794.0]
4 239344 Stokes LLC 91535.92 (89137.708, 100271.535] (90686.0, 95908.0]

Обратите внимание, как сильно различаются интервалы между quantile_ex_1 и quantile_ex_2 . Я также добавил precision (точности), чтобы определить, сколько десятичных знаков использовать для вычисления точности интервала.

Можем посмотреть, как значения распределяются по интервалам с помощью value_counts :

Теперь для второго столбца:

Это иллюстрирует ключевую концепцию: в каждом случае в каждом интервале содержится равное количество наблюдений.

Pandas за кулисами производит вычисления, чтобы определить ширину интервалов. Например, в quantile_ex_1 диапазон первого интервала составляет 74661.15 , а второго - 9861.02 ( 110132 - 100271 ).

Одна из проблем, связанных с этим подходом, заключается в том, что имена интервалов сложно объяснить конечному пользователю.

Например, если мы хотим разделить наших клиентов на 5 групп (также называемых квинтилями), как в случае с часто летающими авиакомпаниями, мы можем явно назвать интервалы, чтобы их было легче интерпретировать:

account number name ext price quantile_ex_1 quantile_ex_2 quantile_ex_3
0 141962 Herman LLC 63626.03 (55733.049000000006, 89137.708] (55732.0, 76471.0] Bronze
1 146832 Kiehn-Spinka 99608.77 (89137.708, 100271.535] (95908.0, 100272.0] Gold
2 163416 Purdy-Kunde 77898.21 (55733.049000000006, 89137.708] (76471.0, 87168.0] Bronze
3 218895 Kulas Inc 137351.96 (110132.552, 184793.7] (124778.0, 184794.0] Diamond
4 239344 Stokes LLC 91535.92 (89137.708, 100271.535] (90686.0, 95908.0] Silver

В приведенном выше примере я сделал кое-что иначе.

Во-первых, явно определил диапазон используемых квантилей: q=[0, .2, .4, .6, .8, 1] , а также задал метки labels=bin_labels_5 для использования при представлении интервалов.

Давайте проверим распределение:

Как и ожидалось, теперь у нас есть равное распределение клиентов по 5 интервалам, а результаты отображаются в простой для понимания форме.

При использовании qcut следует помнить об одном важном моменте: все квантили должны быть меньше 1 . Вот несколько примеров распределений. В большинстве случаев проще определить q как целое число:

  • терцили: q = [0, 1/3, 2/3, 1] или q=3
  • квинтили: q = [0, .2, .4, .6, .8, 1] или q=5
  • секстили: q = [0, 1/6, 1/3, .5, 2/3, 5/6, 1] или q=6 .

Может возникнуть вопрос: как узнать, какие диапазоны используются для идентификации различных интервалов?

В этом случае можно использовать retbins=True для возврата меток интервалов.

Вот полезный фрагмент кода для создания быстрой справочной таблицы:

Threshold Tier
0 55733.050 Bronze
1 87167.958 Silver
2 95908.156 Gold
3 103605.970 Platinum
4 112290.054 Diamond

Вот еще один трюк, которому я научился при написании этой статьи.

account number name ext price quantile_ex_1 quantile_ex_2 quantile_ex_3
0 141962 Herman LLC 63626.03 (55733.049000000006, 89137.708] (55732.0, 76471.0] Bronze
1 146832 Kiehn-Spinka 99608.77 (89137.708, 100271.535] (95908.0, 100272.0] Gold
2 163416 Purdy-Kunde 77898.21 (55733.049000000006, 89137.708] (76471.0, 87168.0] Bronze
3 218895 Kulas Inc 137351.96 (110132.552, 184793.7] (124778.0, 184794.0] Diamond
4 239344 Stokes LLC 91535.92 (89137.708, 100271.535] (90686.0, 95908.0] Silver

Если вы попробуете df.describe для категориальных значений, то получите разные итоговые результаты:

quantile_ex_1 quantile_ex_2 quantile_ex_3
count 20 20 20
unique 4 10 5
top (55733.049000000006, 89137.708] (55732.0, 76471.0] Bronze
freq 5 2 4

Думаю, это является хорошим обзором того, как работает qcut .

Раз уж мы обсуждаем describe , то можем использовать аргумент percentiles (процентилей) для определения процентилей, используя тот же формат, который использовали для qcut :

account number ext price
count 20.000000 20.000000
mean 476998.750000 101711.287500
std 231499.208970 27037.449673
min 141962.000000 55733.050000
0% 141962.000000 55733.050000
33.3% 332759.333333 91241.493333
50% 476006.500000 100271.535000
66.7% 662511.000000 104178.580000
100% 786968.000000 184793.700000
max 786968.000000 184793.700000

Есть одно небольшое замечание.

Передача 0 или 1 означает, что 0% будет таким же, как минимум, а 100% будет таким же, как и максимум.

Я также узнал, что 50-й процентиль всегда будет включен, независимо от переданных значений.

Прежде чем мы перейдем к описанию функции cut , есть еще один потенциальный способ назвать интервалы. Вместо диапазонов интервалов или пользовательских меток мы можем возвращать целые числа, передав labels=False :

account number name ext price quantile_ex_1 quantile_ex_2 quantile_ex_3 quantile_ex_4
0 141962 Herman LLC 63626.03 (55733.049000000006, 89137.708] (55732.0, 76471.0] Bronze 0
1 146832 Kiehn-Spinka 99608.77 (89137.708, 100271.535] (95908.0, 100272.0] Gold 2
2 163416 Purdy-Kunde 77898.21 (55733.049000000006, 89137.708] (76471.0, 87168.0] Bronze 0
3 218895 Kulas Inc 137351.96 (110132.552, 184793.7] (124778.0, 184794.0] Diamond 4
4 239344 Stokes LLC 91535.92 (89137.708, 100271.535] (90686.0, 95908.0] Silver 1

Лично я считаю, что использование bin_labels является наиболее полезным сценарием, но могут быть случаи, когда целочисленный ответ оказывается полезным.

Теперь, когда мы обсудили, как использовать qcut , можем показать, чем он отличается от cut .

Основное различие заключается в том, что qcut будет вычислять размер каждого интервала, чтобы гарантировать, что распределение данных в интервалах одинаково. Другими словами, все интервалы будут иметь (примерно) одинаковое количество наблюдений, но диапазон интервалов будет изменяться.

С другой стороны, cut используется для определения границ интервалов. Нет никаких гарантий относительно распределения элементов в каждом интервале. Фактически, вы можете определить интервалы таким образом, чтобы в них не включались никакие элементы или почти все элементы находились в одном интервале.

В реальных примерах интервалы (bins) могут определяться, исходя из задачи. Для программы часто летающих пассажиров 25 000 миль - это серебряный уровень, который не меняется в зависимости от годового изменения данных. Если мы хотим определить границы интервала ( 25 000 – 50 000 и т.д.), то должны использовать cut .

Можем использовать cut для определения интервалов постоянного размера и позволить pandas определить границы интервалов.

у меня есть код, который я хочу разделить на несколько файлов. В matlab можно просто вызвать .m файл, и пока он не определен как что-либо конкретное, он будет просто работать, как если бы он был частью вызываемого кода. Пример (отредактировано):

Я сейчас исследую использование модулей в python и подумал, что отвечу на вопрос Маркуса в комментариях выше ("как импортировать переменные, когда они встроены в модули?") с двух точек зрения:

  1. функции переменной/, и
  2. свойство/метод класса.

вот как я бы переписал основную программу f1.py чтобы продемонстрировать повторное использование переменных для Markus:

вот как я бы переписал многоразовые f2.py модуль:

когда f1.py выполняется вот как будет выглядеть вывод:

Я думаю, что смысл Маркуса был бы:

  • чтобы повторно использовать код модуля более одного раза, поместите код вашего модуля в функции или классы,
  • чтобы повторно использовать переменные, хранящиеся как свойства в модулях, инициализируйте свойства внутри класса и добавьте методы" getter "и" setter " так переменные не нужно копировать в main программа,
  • для повторного использования переменных, хранящихся в модулях, инициализируйте переменные и используйте функции геттера и сеттера. Функции setter объявят переменные как глобальные.

теперь этот файл ("test.py") в терминологии python является "модулем". Мы можем импортировать его (до тех пор, пока он может быть найден в нашем PYTHONPATH ) обратите внимание, что текущий каталог всегда находится в PYTHONPATH , Так что если use_test выполняется из того же каталога, где test.py жизни, вы все готовы:

вы можете сделать гораздо больше, чем просто с помощью специального __init__.py файлы, которые позволяют обрабатывать несколько файлов как один модуль), но это отвечу на твой вопрос, а остальное оставим на потом.

Python имеет импорт и пространство имен, что хорошо. В Python вы можете импортировать в текущее пространство имен, например:

или с пространством имен:

вы можете сделать то же самое в python, просто импортировав второй файл, код на верхнем уровне будет работать при импорте. Я бы предположил, что это в лучшем случае беспорядочно, а не хорошая практика программирования. Вам было бы лучше организовать свой код в модули

редактировать для уточнения: я предлагаю: "лажа" с помощью import оператор только для побочного эффекта генерации вывода, а не для создания отдельных исходных файлов.

Для больших программ, чтобы быть более организованными, я пытался разделить свой код на разные файлы .py и иметь основной файл, который вызывает эти файлы при необходимости. Я посмотрел вокруг и увидел множество замечаний о создании каталога и SystemPath для Python. Это разумные варианты для программы, которая может быть распределена между несколькими компьютерами? В качестве теста я попытался собрать пример

Это мой основной сценарий в настоящее время

У меня есть несколько вопросов об этом методе:

1: есть ли способ импортировать все из другого файла или мне нужно указать каждый отдельный класс?

  1. Если у меня только что была функция, возможно ли импортировать ее как функцию или вы можете импортировать только через класс?

3. Почему, когда я вызываю класс в целом, я должен создать для него переменную, как в примере ниже?

Спасибо, что уделили время и подумали над моим вопросом.

Вы можете импортировать экземпляр student из другого скрипта в основной скрипт следующим образом:

2 . Если у вас есть функция test() в grades.py, вы можете импортировать ее следующим образом:

3. Эта переменная обозначает экземпляр класса ученика. Вам нужен этот экземпляр для вызова функции внутри.

Во-первых, вы можете использовать from module import * для импорта всего, например: hello.py :

Но это не очень хороший способ, если у вас есть два файла, две функции имеют одинаковое имя, поэтому используйте

Лучше всего, это пример для функции, такой же как класс.

Третий перед Вторым, ученик - это класс, поэтому вы должны использовать объект экземпляра, чтобы использовать функцию, которая с параметром self. Если вы хотите использовать student.function, функция должна быть статическим методом, подобным этому:

Во-вторых, вы можете импортировать функцию в файл класса, который не зависит от класса.

  1. Есть ли способ импортировать все из другого файла или мне нужно указать каждый отдельный класс?

Ответ - да, поскольку в выражении python import используется > (список строк, в котором указывается путь поиска модулей), вам нужно добавить путь ваших модулей в sys.path , например, если вы хотите взаимодействовать между различными компьютерами, вы можете поместить свои модули в папке public и добавьте путь к папке sys.path :

  1. Если у меня только что была функция, можно ли импортировать ее как функцию или можно импортировать только через класс?

Вам просто нужно использовать from . import function_name .

  1. Почему, когда я вызываю класс в целом, я должен создать для него переменную, как в примере ниже?

По этому вопросу вам просто нужно прочитать питон Class objects документация:

Объекты класса поддерживают два вида операций: ссылки на атрибуты и создание экземпляров.

1: есть ли способ импортировать все из другого файла или мне нужно указать каждый отдельный класс?

Вы можете использовать «импорт по шаблону», но вам, вероятно, не следует. Увидеть Следует ли избегать импорта подстановочных знаков?

  1. Если бы у меня была функция, можно ли ее импортировать как функция или вы можете импортировать только через класс?

Функции могут быть полностью независимыми от классов в Python.

3. Почему, когда я вызываю класс в целом, я должен создать для него переменную, как в примере ниже?

Вы должны прочитать об объектно-ориентированном программировании. В основных случаях вы должны создавать экземпляры класса, чтобы использовать функциональные возможности этого класса. В вашем примере ученик класса описывает, что значит быть учеником, и утверждение

Создает ученика и называет его "с". Я думаю, что это должно быть ясно после прочтения немного об объектно-ориентированном программировании.

Обычно, чтобы разделить исходный код программы, Python использует для этого модуль , который соответствует файлу * .py. Тогда для ваших 3 вопросов:

    Вы можете импортировать весь «контент модуля» (функция, класс, глобальные переменные, . ) через import module_name.*

для функции, если это функция в классе (метод-член, метод класса или статический метод), вы можете не только импортировать функцию, вы должны импортировать класс, чтобы использовать метод; если это функция в модуле, вы можете отдельно импортировать функцию через import module_name.function_name

update является функцией-членом класса ученика, поэтому вы должны использовать его через экземпляр. если это метод класса или статический метод, вы можете использовать его через имя класса, которое вы написали.

У меня есть код, который я хочу разделить на несколько файлов. В Matlab можно просто вызвать файл .m , и до тех пор, пока он не определен как что-либо в частности, он просто запустится так, как если бы он был частью вызываемого кода. Пример (отредактированный):
test.m (matlab)

test2.m (matlab)

Вызов test запускает код в тесте, а также код в test2.

Есть ли аналогичный способ для python поместить . some more code . во внешний файл, который будет просто читаться так, как если бы он находился в файле, из которого он вызван?

Прочитайте ваш любимый учебник по Python и узнайте о модулях

4 ответа

Я изучаю использование модуля в python только сейчас и думал, что отвечу на вопрос, который Маркус просит в комментариях выше ( "Как импортировать переменные, когда они встроены в модули?" ) с двух сторон:

  • переменная/функция и
  • свойство/метод класса.

Вот как я переписал основную программу f1.py, чтобы продемонстрировать повторное использование переменной для Markus:

Вот как я могу переписать повторно используемый модуль f2.py:

Когда выполняется f1.py, это будет выглядеть так:

Я думаю, что точка Маркуса была бы следующей:

  • Чтобы повторно использовать код модуля более одного раза, введите код модуля в функций или классов,
  • Чтобы повторно использовать переменные, хранящиеся как свойства в модулях, инициализируйте свойства внутри класса и добавить методы "getter" и "setter", чтобы переменные не обязательно должны быть скопированы в основную программу,
  • Чтобы повторно использовать переменные, хранящиеся в модулях, инициализируйте переменные и используйте функции геттера и сеттера. Функции сеттера объявили бы переменные как глобальные.

Теперь этот файл ( "test.py" ) находится в терминологии python как "модуль". Мы можем импортировать его (пока он может быть найден в нашем PYTHONPATH ). Обратите внимание, что текущий каталог всегда находится в PYTHONPATH , поэтому, если use_test выполняется из того же каталога, где живет test.py re all set:

Вы можете сделать гораздо больше, чем просто с помощью специальных файлов __init__.py , которые позволяют обрабатывать несколько файлов как один модуль), но это отвечает на ваш вопрос, и я полагаю, что мы оставим все остальное для в другой раз.

В прошлой статье я написал о некоторых методах, которые я использую в python для эффективной обработки очень больших наборов данных. Вы можете найти это здесь. Грубо говоря, в нем подробно рассказывается, как можно разбить большой файл на куски, которые затем можно передать на несколько ядер для сокращения времени обработки. Ниже я подробно остановлюсь на этом, сначала создав родительский класс, который превращает данный (большой) файл в порции. Я создаю его таким образом, чтобы дочерние классы можно было легко создавать и адаптировать для конкретных типов файлов, учитывая некоторые примеры. Наконец, я даю некоторые функции обертывания для использования в сочетании с любым из чанков, чтобы чанки могли обрабатываться с использованием нескольких ядер.

Есть один вопрос на который стоит дать ответ, перед тем как вы начнете использовать данный метод повсеместно. Какого размера фалй стоит обрабатывать данным методом? Грубый ответ будет, когда размер данных становится сопоставимым с доступной оперативной памятью. Лучшим ответом будет, когда издержки на чтение каждой отдельной строки (записи) больше, чем операция над этой записью. Вот пример этого случая, хотя это не совсем справедливое сравнение:

Для небольшого 10-мегабайтного файла Fasta мы подсчитываем количество последовательностей, присутствующих в пятой части времени, используя чанки. Я должен быть честным и утверждать, что ускорение в основном связано с отсутствием идентифицирующих символов новой строки в методе чанкинга; но тем не менее, это показывает силу, которую можно иметь, используя куски. Для файла fasta 14 ГБ время для фрагментированных (1 МБ фрагментов) и не фрагментированных методов составляет 55 с и 130 с соответственно.

Возвращаясь на правильный путь, давайте превратим метод chunking в родительский класс, из которого мы можем построить:

Выше мы создаем класс Chunker, у которого есть метод класса chunkify, а также статические методы _EOC, read и parse. Метод chunkify выполняет фактическое разбиение на фрагменты данных файла, возвращая итератор, который выдает кортежи, содержащие начало и размер фрагмента. Это метод класса, так что он может использовать статический метод _EOC (конец фрагмента), чтобы переместить указатель в подходящее место для разделения фрагментов. Для простейшего случая это просто конец / начало новой строки. Методы read и parse считывают данный фрагмент из файла и разбивают его на блоки (отдельные строки в простейшем случае) соответственно. Мы делаем методы non-chunkify статическими, чтобы их можно было вызывать без дополнительных затрат на создание экземпляра класса.

Давайте теперь создадим несколько дочерних элементов этого класса для определенных типов файлов. Во-первых, один из самых известных типов файлов в биоинформатике, FASTA. Ниже приведен фрагмент файла FASTA. Каждая запись имеет строку заголовка, которая начинается с «>», за которым следует уникальный идентификатор последовательности. После строки заголовка следуют одна или несколько строк, задающих последовательность. Последовательности могут представлять собой последовательности белков или нуклеиновых кислот, и они могут содержать пробелы и / или символы выравнивания.

А вот обработка данного файла с применением нашего класса Chunker:

Мы обновляем метод _EOC, чтобы найти, когда одна запись заканчивается, а следующая начинается с определения местоположения «>», после чего мы перематываем указатель дескриптора файла на начало этой строки. Мы также обновляем метод parse, чтобы использовать анализатор fasta из модуля BioPython, что дает объекты SeqRecord для каждой записи в чанке.

В качестве второго более сложного примера приведу пример работы с выводом, полученным с помощью bowtie, выравнивателя коротких операций чтения из данных NGS. Формат состоит из столбцов, разделенных табуляцией, с идентификатором каждого чтения, расположенным в первом столбце. Обратите внимание, что одно чтение может быть выровнено в нескольких местах (до 8 по умолчанию!), Поэтому один и тот же идентификатор появляется в нескольких строках. Небольшой пример раздела результатов приведен ниже.

с соответствующим Chunker, заданным:

На этот раз конец фрагмента находится путем чтения строк до тех пор, пока в идентификаторе чтения не появится переключатель, после чего мы возвращаем одну строку. Для синтаксического анализа мы приводим все различные местоположения, к которым относится данное чтение, как одну запись.

Надеемся, что эти примеры покажут, как можно легко расширить родительский класс для большинства типов файлов. Давайте теперь объединим эти различные чанкеры с кодом из предыдущего поста, чтобы показать, как мы можем включить многоядерную параллельную обработку чанков, которые они дают. Приведенный ниже код содержит несколько обобщенных функций-оболочек, которые работают в тандеме с любым из вышеперечисленных блоков, позволяя распараллелить большинство задач.

Основная функция должна быть узнаваемой как код из предыдущего поста. Он генерирует пул воркеров, в идеале по одному на каждое ядро, прежде чем использовать данный блок для разделения соответствующего файла на серию фрагментов для обработки. В отличие от предыдущих примеров, мы собираем выходные данные каждой задачи и возвращаем их после завершения обработки. Основная функция действует как оболочка, позволяющая нам определять разные функции обработки и разные чанкеры, как указано переменными worker и chunker соответственно. Мы обернули вызов функции обработки в функцию _workerMP, которая печатает на терминал по мере выполнения задач. Для этого он использует функцию _printMP, так как вам нужно очистить терминал после оператора печати при использовании многоядерной обработки, иначе ничего не появится, пока все задачи не будут выполнены.

Давайте закончим, продемонстрировав пример того, как мы будем использовать вышеупомянутое, чтобы выполнить ту же задачу, что и в начале этого поста, подсчитывая последовательности в файле fasta, используя базовый чанкер:

и используя чанкер FASTA:

И время, которое они занимают, чтобы посчитать последовательности в файле 14GB из ранее:

Давайте проигнорируем последний вывод, поскольку замедление связано с превращением записей в BioPython SeqRecords. Предыдущий, который объединяет разбиение на чанки и многоядерную обработку, примерно в 50 раз быстрее. Я уверен, что это может быть дополнительно уменьшено с использованием большего количества ядер и / или оптимизации размера фрагмента, однако одно это различие может изменить что-то от вычислительно неправдоподобного к правдоподобному. Неплохо только для нескольких строк кода.

Читайте также: