Как перемножить столбцы в датафрейме r
Перейти к содержимому

Как перемножить столбцы в датафрейме r

  • автор:

Как сложить столбцы фрейма данных в R

Часто вам может понадобиться объединить два или более столбца фрейма данных в один столбец в R.

Например, вы можете перейти от этого:

person trial outcome1 outcome2 A 1 7 4 A 2 6 4 B 1 6 5 B 2 5 5 C 1 4 3 C 2 4 2 
person trial outcomes value A 1 outcome1 7 A 2 outcome1 6 B 1 outcome1 6 B 2 outcome1 5 C 1 outcome1 4 C 2 outcome1 4 A 1 outcome2 4 A 2 outcome2 4 B 1 outcome2 5 B 2 outcome2 5 C 1 outcome2 3 C 2 outcome2 2 

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

Метод 1: используйте функцию стека в Base R

В следующем коде показано, как складывать столбцы с помощью функции стека в базе R:

#create original data frame data  

Способ 2: используйте функцию плавления от Reshape2

В следующем коде показано, как сложить столбцы с помощью функции плавления из библиотеки reshape2 :

#load library library(reshape2) #create original data frame data  

Полную документацию по функции Melt можно найти здесь .

7 полезных операций в Pandas при работе с DataFrame

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

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

1. Конкатенация DataFrame

Есть два способа конкатенировать датафрейм A и B . Представьте их как проиндексированные таблицы. Эти две таблицы можно объединить либо по оси y, либо по оси x.

Если требуется конкатенировать их вдоль x, то вызов API будет таким:

import pandas as pdpd.concat([A, B], axis=1)

Если же вдоль y, то таким:

import pandas as pdpd.concat([A, B]) # по умолчанию axis = 0 

Применение

Предположим, у вас есть большое количество CSV-файлов или XLSX-данных, которые нужно присоединить друг к другу. Одним из способов сделать это будет считать данные файлов в датафреймы и использовать инструкцию pd.concat([file_1, file_2]) . Программно можно перебрать имена файлов (используя модуль glob для чтения набора имен файлов с помощью техники сопоставления шаблонов, например regex), считать их в датафрейм, соединить в памяти и сериализовать конкатенированные датафреймы в нужный формат файлов.

Вариант с axis = 0 используется нечасто, но его можно применять в сценариях, когда нужно обработать массивы данных, собранных с упорядочиванием. То есть, когда последовательность данных соответствует последовательности других массивов данных. В таком случае эти массивы можно объединить вдоль оси x, получив более объемное и значительное представление в табличном формате. Затем к полученной структуре можно применять операции, использующие все типы данных в ее столбцах.

2. Разделение DataFrame

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

Просмотр сведений

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

import pandas as pdprint(df.head(10)) # выводит первые 10 строк dataframe

Исключение столбцов

Этот метод разделяет датафрейм вдоль оси y, то есть просто выбрасывает из него часть столбцов. Используется данный метод в типичном сценарии, когда нам не нужно, чтобы конечный DataFrame содержал эти столбцы, или когда мы предполагаем, что при дальнейшем обновлении структура станет занимать слишком много памяти.

import pandas as pddf.drop('COLUMN_NAME', inplace=True, axis=1) 
# указывает, что 'COLUMN_NAME' находится на оси x

Удаление датафреймов друг из друга

Представим, что у нас есть датафрейм X , состоящий из столбцов [A, B, C, D] , и датафрейм Y , состоящий из подмножества столбцов X . Нам нужно удалить Y из X . Это можно сделать так:

import pandas as pdpd.concat([X, Y]).drop_duplicates(keep=False)

Конкатенация этих датафреймов приведет к дублированию общих записей, которые в итоге будут удалены выражением keep = false функции drop_duplicates() .

Применение

Предположим, что столбец A — это определенный вид ID сведений о работнике. К примеру, датафрейм X состоит из всех данных о работниках, а датафрейм Y содержит данные (с той же структурой) о работниках, не разбирающихся в Python. Нам нужно отфильтровать сведения о сотрудниках, которые не знакомы с Python.

Определение дельты записей на основе столбца

Представим, что у нас есть датафрейм X , состоящий из столбцов [A, B, C, D] , а также датафрейм Y , состоящий из тех же столбцов. При этом некоторые элементы столбцов A этих датафреймов являются общими. Нам нужно получить из датафрейма X строки, которые не содержат значения из столбца A , находящиеся в столбце A датафрейма Y .

import pandas as pdX[~X['A'].isin(Y['A'])]

Применение

Взгляните на эту таблицу:

Эти пары могли быть сгенерированы, например, из двух журналов: старого и нового. Нам нужно найти пары ID, принадлежащие одному и тому же человеку. Предположим, что ваш отдел кадров неожиданно заявляет, что определенный список ( hr_list ) сотрудников с ID_1 больше в компании не работает. Как удалить их из этого датафрейма?

import pandas as pdfiltered_pairs = employee_pairs[~employee_pairs['ID_1'].isin(hr_list)]

Разделение на основе значений столбцов

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

import pandas as pdfiltered_df = df[~df['A'].isna() & (df['B'] > 10)]
# Возвращает строки, где столбец A не null, а значение столбца B больше 10.

Это простейший пример.

3. Подсчет записей в столбце

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

Ответ на приведенный выше запрос можно получить следующим подходом:

import pandas as pd
df = df[ (1984 df.columns = ['Contry', 'Count']
"nbformat": 4,
"nbformat_minor": 0,
"metadata": "colab": "name": "Host Countries.ipynb",
"provenance": []
>,
"kernelspec": "name": "python3",
"display_name": "Python 3"
>
>,
"cells": [
"cell_type": "code",
"metadata": "id": "502dBbjWHq89"
>,
"source": [
"import pandas as pd\n",
"\n",
"df = pd.read_excel('Asia Cup Winners in Cricket.xlsx')"
],
"execution_count": null,
"outputs": []
>,
"cell_type": "code",
"metadata": "colab": "base_uri": "https://localhost:8080/"
>,
"id": "14WfNdQRIjWw",
"outputId": "2b1e8cc9-75c1-40a5-8d2b-b45ed1641723"
>,
"source": [
"df.shape"
],
"execution_count": null,
"outputs": [
"output_type": "execute_result",
"data": "text/plain": [
"(13, 7)"
]
>,
"metadata": "tags": []
>,
"execution_count": 9
>
]
>,
"cell_type": "code",
"metadata": "id": "eSwxxIeTH90N"
>,
"source": [
"df = df[ (1984 ],
"execution_count": null,
"outputs": []
>,
"cell_type": "code",
"metadata": "colab": "base_uri": "https://localhost:8080/"
>,
"id": "1X6LwBL-IBcB",
"outputId": "9277501d-c02e-4345-a38c-66371d50979d"
>,
"source": [
"df.shape"
],
"execution_count": null,
"outputs": [
"output_type": "execute_result",
"data": "text/plain": [
"(12, 7)"
]
>,
"metadata": "tags": []
>,
"execution_count": 11
>
]
>,
"cell_type": "code",
"metadata": "id": "ZC1nYNy1IaT5"
>,
"source": [
"df = df['Host'].value_counts().reset_index()\n",
"df.columns = ['Country', 'Counts']"
],
"execution_count": null,
"outputs": []
>,
"cell_type": "code",
"metadata": "colab": "base_uri": "https://localhost:8080/",
"height": 204
>,
"id": "PdFw0MMkIwDH",
"outputId": "8daa83cb-af9f-41aa-b3e9-71d6a948de51"
>,
"source": [
"df"
],
"execution_count": null,
"outputs": [
"output_type": "execute_result",
"data": "text/html": [
"
\n",
"\n",
"\n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
"
CountryCounts
0Sri Lanka4
1Bangladesh4
2UAE2
3Pakistan1
4India1
\n",
"
"
],
"text/plain": [
" Country Counts\n",
"0 Sri Lanka 4\n",
"1 Bangladesh 4\n",
"2 UAE 2\n",
"3 Pakistan 1\n",
"4 India 1"
]
>,
"metadata": "tags": []
>,
"execution_count": 13
>
]
>
]
>

4. Чтение фрагментов DataFrame

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

import pandas as pdpd.read_csv('file.csv', usecols=['A', 'B'])
# Считать только столбцы 'A' и 'B'

Более того, можно считывать большие файлы в отдельные фрагменты и маршалировать их в датафреймы.

import pandas as pdfor chunk in pd.read_csv('file.csv', chunksize=1000): 
process(chunk)

Таким образом одновременно в памяти удерживается только фрагмент размером chunksize .

5. Применение функций к строкам

Бывают случаи, в которых требуется внести изменения в конкретные столбцы детафрейма. К примеру, в датафрейме X , содержащем столбцы A , B и C , мы можем применить функцию f() к значениям столбца B , чтобы сохранить их в столбце D .

import pandas as pddf['D'] = df.apply(lambda row: f(row['B'], row['C']), axis=1)

Эта операция окажется намного быстрее, чем перебор всего датафрейма с помощью iterrows() .

Есть и альтернативный метод. Его можно использовать, когда функцию f() требуется применить только к одному столбцу.

import pandas as pddf['D'] = df['B'].map(lambda b: f(b))

6. Объединение двух датафреймов

По аналогии с реляционными базами данных датафреймы можно объединять merge , используя разрешающий столбец.

import pandas as pdpd.merge(left=df_a, left_on['A'], right=df_b, right_on=['B'], how='inner')
# Также работает для left и right объединения.

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

7. Переименование столбцов

Переименовывать столбцы особенно полезно перед сериализацией файла или перед внедрением стороннего хранилища данных.

import pandas
df = df.rename(columns= "A_1": "A"
"B_1": "B"
>)
# переименовывает оригинальные столбцы ["A_1", "B_1"] в ["A", "B"].
  • Новая библиотека превосходит Pandas по производительности
  • 7 трюков pandas для науки о данных
  • Хватит использовать Pandas, пора переходить на Spark + Scala!

Разработка на R: тайны циклов

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

Примечание: орфография и пунктуация автора сохранены.

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

Сегодня рассмотрим особенности применения штатных циклов в R, а также познакомимся с функцией foreach из одноименного пакета, которая предлагает альтернативный подход в этой, казалось бы, базовой задаче. С одной стороны, foreach объединяет лучшее из штатной функциональности, с другой — позволяет с легкостью перейти от последовательных вычислений к параллельным с минимальными изменениями в коде.

О циклах

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

Чтобы понять, насколько важно это правило, давай обратимся к цифрам. Допустим, у нас есть очень простая таблица из двух столбцов a и b . Первый растет от 1 до 100 000, второй уменьшается со 100 000 до 1:

testDF  

Если мы хотим посчитать третий столбец, который будет суммой первых двух, то ты удивишься, как много начинающих R-разработчиков могут написать код такого вида:

for(row in 1:nrow(testDF)) testDF[row, 3] 

На моем ноутбуке расчеты занимают 39 секунд, хотя того же результата можно достичь за 0,009 секунды, воспользовавшись функцией для работы с таблицами из пакета dplyr :

testDF % mutate(c = a + b)

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

Классические циклы

R поддерживает основные классические способы написания циклов:

    for — самый распространенный тип циклов. Синтаксис очень прост и знаком разработчикам на различных языках программирования. Мы уже пробовали им воспользоваться в самом начале статьи. for выполняет переданную ему функцию для каждого элемента.

# Напечатаем номера от 1 до 10 for(i in 1:10) print(i) # Напечатаем все строки из вектора strings strings 
while(cond) expr

В repeat цикл повторяется до тех пор, пока в явном виде не будет вызван оператор break :

 repeat expr

Стоить отметить, что for , while и repeat всегда возвращают NULL, — и в этом их отличие от следующей группы циклов.

Циклы на основе apply

apply , eapply , lapply , mapply , rapply , sapply , tapply , vapply — достаточно большой список функций-циклов, объединенных одной идеей. Отличаются они тем, к чему цикл применяется и что возвращает.

Начнем с базового apply , который применяется к матрицам:

apply(X, MARGIN, FUN, . )

В первом параметре ( X ) указываем исходную матрицу, во втором параметре ( MARGIN ) уточняем способ обхода матрицы (1 — по строкам, 2 — по столбцам, с(1,2) — по строкам и столбцам), третьим параметром указываем функцию FUN, которая будет вызвана для каждого элемента. Результаты всех вызовов будут объединены в один вектор или матрицу, которую функция apply и вернет в качестве результирующего значения.

Например, создадим матрицу m размером 3 х 3.

Попробуем функцию apply в действии.

apply(m, MARGIN = 1, FUN = sum) # Сумма ячеек для каждой строчки [1] 12 15 18 apply(m, MARGIN = 2, FUN = sum) # Сумма ячеек для каждого столбца [1] 6 15 24

Для простоты я передал в apply существующую функцию sum , но ты можешь использовать свои функции — собственно, поэтому apply и является полноценной реализацией цикла. Например, заменим сумму нашей функцией, которая сначала производит суммирование и, если сумма равна 15, заменяет возвращаемое значение на 100.

apply(m, MARGIN = 1, # Вызов нашей функции для каждой строчки FUN = function(x) # Определяем нашу функцию прямо в вызове apply < s ) [1] 12 100 18

Другая распространенная функция из этого семейства — lapply .

lapply(X, FUN, . )

Первым параметром передается список или вектор, а вторым — функция, которую надо вызвать для каждого элемента. Функции sapply и vapply — это обертки вокруг lapply . Первая пытается привести результат к вектору, матрице или массиву. Вторая добавляет проверку типов возвращаемого значения.

Достаточно распространен такой способ применения sapply , как работа с колонками. Например, у нас есть таблица

data 

При передаче sapply таблицы она будет рассматриваться как список колонок (векторов). Поэтому, применив sapply к нашему data.frame и указав в качестве вызываемой функции is.numeric , мы проверим, какие столбцы являются числовыми.

sapply(data, is.numeric) co1_num col2_num col3_char col4_char TRUE TRUE FALSE FALSE

Выведем на экран только столбцы с числовыми значениями:

data[,sapply(data, is.numeric)] co1_num col2_num 1 1 2

Циклы, основанные на apply , отличаются от классических тем, что возвращается результат работы цикла, состоящий из результатов каждой итерации.

Помнишь тот медленный цикл, что мы написали в самом начале с помощью for ? Большая часть времени терялась на то, что на каждой итерации в таблицу записывались результаты. Напишем оптимизированную версию с использованием apply .

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

a_plus_b 

Замер времени исполнения показывает 0,248 секунды, что в сто раз быстрее первого варианта, но все еще в десять раз медленнее функций операций с таблицами.

foreach

foreach — не базовая для языка R функция. Соответствующий пакет необходимо установить, а перед вызовом подключить:

install.packages("foreach") # Установка пакета на компьютер (один раз) library(foreach) # Подключение пакета

Несмотря на то что foreach — сторонняя функция, на сегодняшний день это очень популярный подход к написанию циклов. foreach был разработан одной из самых уважаемых в мире R компанией — Revolution Analytics, создавшей свой коммерческий дистрибутив R. В 2015 году компания была куплена Microsoft, и сейчас все ее наработки входят в состав Microsoft SQL Server R Services. Впрочем, foreach представляет собой обычный open source проект под лицензией Apache License 2.0.

Основные причины популярности foreach :

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

Начнем c простого. Для чисел от 1 до 10 на каждой итерации число умножается на 2. Результаты всех итераций записываются в переменную result в виде списка:

result 

Если мы хотим, чтобы результатом был не список, а вектор, то необходимо указать c в качестве функции для объединения результатов:

result 

Можно даже просто сложить все результаты, объединив их с помощью оператора + , и тогда в переменную result будет просто записано число 110:

result 

При этом в foreach можно указывать одновременно несколько переменных для обхода. Пусть переменная a растет от 1 до 10, а b уменьшается от 10 до 1. Тогда мы получим в result вектор из 10 чисел 11:

result 

Итерации циклов могут возвращать не только простые значения. Допустим, у нас есть функция, которая возвращает data.frame :

customFun

Если мы хотим вызвать эту функцию сто раз и объединить результаты в один data.frame , то в .combine для объединения можно использовать функцию rbind :

result 

В результате в переменной result у нас собрана единая таблица результатов.

В .combine возможно также использовать свою собственную функцию, причем с помощью дополнительных параметров можно оптимизировать производительность, если твоя функция умеет принимать больше чем два параметра сразу (в документации foreach есть описание параметров .multicombine и .maxcombine ).

Одно из главных преимуществ foreach заключается в легкости перехода от последовательной обработки к параллельной. Фактически этот переход осуществляется заменой %do% на %dopar% , но при этом есть несколько нюансов:

    До вызова foreach у тебя уже должен быть зарегистрирован parallel backend. В R есть несколько популярных реализаций parallel backend doParallel , doSNOW , doMC , и у каждого есть свои особенности, но предлагаю ради простоты выбрать первый и написать несколько строчек кода для его подключения:

library(doParallel) # Загружаем библиотеку в память cl 

Если сейчас вызвать цикл из восьми итераций, каждая из которых просто ждет одну секунду, то будет видно, что цикл отработает за одну секунду, так как все итерации будут запущены параллельно:

 system.time(< foreach(i=1:8) %dopar% Sys.sleep(1) # >) user system elapsed 0.008 0.005 1.014 

После использования parallel backend можно остановить:

 stopCluster(cl)

Нет никакой необходимости каждый раз перед foreach создавать, а затем удалять parallel backend. Как правило, он создается один раз в программе и используется всеми функциями, которые могут с ним работать.

  1. Тебе надо явно указать, какие пакеты необходимо загрузить в рабочие потоки с помощью параметра .packages .

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

 library(readr) foreach(i=1:8) %do% write_csv(data.frame(id = 1), paste0("file", i, ".csv")) При переходе на параллельную обработку (`%dopar%`) цикл закончится с ошибкой: library(readr) foreach(i=1:8) %do% write_csv(data.frame(id = 1), paste0("file", i, ".csv")) Error in write_csv(data.frame(id = 1), paste0("file", i, ".csv")) : task 1 failed - "could not find function "write_csv""

Ошибка возникает, поскольку внутри параллельного потока не загружен пакет readr . Исправим эту ошибку с помощью параметра .packages :

 foreach(i=1:8, .packages = "readr") %dopar% write_csv(data.frame(id = 1), paste0("file", i, ".csv"))
  1. Вывод на консоль в параллельном потоке не отображается на экране. Иногда это может здорово усложнить отладку, поэтому обычно сложный код сначала пишут без параллельности, а потом заменяют %do% на %dopar% либо перенаправляют вывод каждой итерации в свой файл с помощью функции sink .

Вместо выводов

  • При работе с большим объемом данных циклы не всегда оказываются лучшим выбором. Использование специализированных функций для выборки, агрегации и трансформации данных всегда эффективнее циклов.
  • R предлагает множество вариантов реализации циклов. Основное отличие классических for , while и repeat от группы функций на основе apply заключается в том, что последние возвращают значение.
  • Использование циклов foreach из одноименного внешнего пакета позволяет упростить написание циклов, гибко оперировать возвращаемыми итерациями значениями, а за счет подключения многопоточной обработки еще и здорово увеличить производительность решения.
  • Официальная документация пакета foreach
  • Официальный обзор функциональности foreach
  • Анализ данных с использованием R. Часть 1
  • Изучаем R. Часть 2: векторизация и визуализация
  • Программируем на языке R: как правильно писать циклы для обработки больших объемов данных

Сложение определенных столбцов в Pandas

Доброго времени суток! Прошу помочь, искал на просторах интернета, но так и не понял как конкретно сложить именно нужные столбцы, не трогая другие. Необходимо сложить значения столбов к примеру a,b,c,d в один и назвать к примеру его F.

import pandas as pd data=pd.read_csv("C:\\table_primer.csv", low_memory=False) table=data[["ID","Period","a","b","c","d"]] print(table) 

вот как выводит

 ID Period a b c d 0 1 2 3 4 5 6 1 2 3 4 5 6 7 

Как вывести сложенные значения a,b,c,d под новым столбцом F, при этом не трогая ID и Period( это время), и убрать из таблицы a,b,c,d после сложения в F?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *