Чем data frame отличается от двумерной матрицы
Перейти к содержимому

Чем data frame отличается от двумерной матрицы

  • автор:

Должен ли я использовать data.frame или матрицу?

Когда следует использовать data.frame , и когда лучше использовать matrix ?

Оба сохраняют данные в прямоугольном формате, поэтому иногда это неясно.

Существуют ли какие-либо общие правила для использования какого типа данных?

5 ответов

Часть ответа содержится уже в вашем вопросе: вы используете кадры данных, если можно считать, что столбцы (переменные) могут быть разных типов (числовые/символьные/логические и т.д.). Матрицы предназначены для данных того же типа.

Следовательно, матрица выбора /data.frame является только проблематичной, если у вас есть данные того же типа.

Ответ зависит от того, что вы собираетесь делать с данными в data.frame/matrix. Если он будет передан другим функциям, то ожидаемый тип аргументов этих функций определит выбор.

Матрицы более эффективны с точки зрения памяти:

Матрицы являются необходимостью, если вы планируете выполнять операции типа линейной алгебры.

Кадры данных более удобны, если вы часто ссылаетесь на свои столбцы по имени (через оператор compact $).

Кадры данных также IMHO лучше для представления (печати) табличной информации, так как вы можете применять форматирование для каждого столбца отдельно.

Что-то, не упомянутое в @Michal, заключается в том, что не только матрица, меньшая, чем эквивалентный кадр данных, использование матриц может сделать ваш код намного более эффективным, чем использование фреймов данных, что часто значительно. Это одна из причин, по которой внутри, многие функции R будут принуждать к матрицам данных, которые находятся в кадрах данных.

Кадры данных часто намного удобнее; не всегда есть только атомные куски данных, лежащих вокруг.

Обратите внимание, что вы можете иметь матрицу символов; вам просто не нужно иметь числовые данные для построения матрицы в R.

При преобразовании фрейма данных в матрицу обратите внимание, что существует функция data.matrix() , которая соответствующим образом обрабатывает факторы путем преобразования их в числовые значения на основе внутренних уровней. Принуждение через as.matrix() приведет к матрице символов, если какая-либо из меток-меток не является числовой. Для сравнения:

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

4 Сложные структуры данных в R

Порядок значений имеет значение, но этот порядок одномерный.

Внутри вектора могут быть данные только одного типа

Как вы уже поняли, вектор – это одно из важнейших понятий в R, и он нам будет встречаться дальше постоянно. Обычно работа с данными – это именно работа с векторами, различные операции на векторах.

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

4.1 Матрица

Если вдруг вас пугает это слово, то совершенно зря. Матрица (matrix) – это всего лишь “двумерный” вектор: вектор, у которого есть не только длина, но и ширина. Создать матрицу можно с помощью функции matrix() из вектора, указав при этом количество строк и столбцов.

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

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

Все остальное так же как и с векторами: внутри находится данные только одного типа. Поскольку матрица – это уже двумерный массив, то у него имеется два индекса. Эти два индекса разделяются запятыми.

Первый индекс – выбор строк, второй индекс – выбор колонок 1 . Результат – пересечение выбранных строк и столбцов.

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

Так же как и в случае с обычными векторами, часть матрицы можно переписать:

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

То, что матрица – это просто двумерный вектор, не является метафорой: в R матрица – это по сути своей вектор с дополнительными атрибутами dim и (опционально) dimnames . Атрибуты – это свойства объектов, своего рода “метаданные”. Для всех объектов есть обязательные атрибуты типа и длины и могут быть любые необязательные атрибуты. Можно задавать свои атрибуты или удалять уже присвоенные: удаление атрибута dim у матрицы превратит ее в обычный вектор. Про атрибуты подробнее можно почитать здесь или на стр. 99-101 книги “R in a Nutshell” (Adler 2010) .

4.2 Массив

Два измерения – это не предел! Структура с одним типом данных внутри, но с тремя измерениями или больше, называется массивом (array). Создание массива очень похоже на создание матрицы: задаем вектор, из которого будет собран массив, и размерность массива.

4.3 Список

Теперь представим себе вектор без ограничения на одинаковые данные внутри. И получим список (list)!

А это значит, что там могут содержаться самые разные данные, в том числе и другие списки, векторы и матрицы (и другие объекты, которые нам еще не знакомы)!

Если у нас сложный список, то есть очень классная функция str() , чтобы посмотреть, как он устроен:

Представьте, что список — это такое дерево с ветвистой структурой. А на конце этих ветвей — листья-векторы.

Как и в случае с векторами мы можем давать имена элементам списка:

К списку можно обращаться как с помощью индексов, так и по именам. Начнем с последнего:

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

Мы, по сути, получили элемент списка – просто как часть списка, т.е. как список длиной один:

А вот чтобы добраться до самого элемента списка (и сделать с ним что-то хорошее), нам нужна не одна, а две квадратных скобочки:

Как и в случае с вектором, к элементу списка можно обращаться по имени. Здесь тоже будет иметь значение, одинарные или двойные квадратные скобки вы используете:

Хотя последнее – практически то же самое, что и использование знака $.

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

4.4 Датафрейм

Итак, мы перешли к самому главному. Самому-самому. Датафреймы (data.frames). Более того, сейчас станет понятно, зачем нам нужно было разбираться со всеми предыдущими темами.

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

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

Датафрейм очень похож на список. Просто поменяем в команде выше list() на data.frame() и посмотрим, что изменится:

Вообще, очень похоже на список, не правда ли? Так и есть, датафрейм – это что-то вроде проименованного списка, каждый элемент которого является atomic вектором фиксированной длины. Скорее всего, вы представляли список “горизонтально”. Если это так, то теперь “переверните” список у себя в голове на 90 градусов. Так, чтобы названия векторов оказались сверху, а элементы списка стали столбцами.

Поскольку длина всех этих векторов одинаковая (обязательное условие!), то данные представляют собой табличку, похожую на матрицу. Но в отличие от матрицы, разные столбцы могут иметь разные типы данных. В нашем случае первая колонка – character , вторая колонка – numeric , третья колонка – logical . Тем не менее, обращаться с датафреймом можно и как с проименованным списком, и как с матрицей:

Здесь мы сначала извлекли колонку age с помощью оператора $ . Результатом этой операции является числовой вектор.

Колонки датафрейма – это и есть векторы.

Теперь с ним можно работать как с обычным вектором: мы вытащили кусок, выбрав индексы 2 и 3 .

Используя оператор $ и присваивание можно создавать новые колонки датафрейма:

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

Как и с матрицами, первый индекс означает строчки, а второй – столбцы.

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

И здесь перед нами открываются невообразимые возможности! Узнаем, любят ли R те, кто моложе среднего возраста в группе:

Обратите внимание, как удобно нам здесь пригодилось то, что мы научились делать с векторами ( Глава 3 ). Сначала мы посчитали среднее значение абсолютно так же, как мы делали это с векторами:

Полученное среднее поэлементно сравнили с каждым значением колонки (т.е. вектора) df$age :

Мы получили логический вектор, длина которого совпадает с длиной датафрейма. При этом TRUE стоит на тех позициях, где в соответствующей строчке в датафрейме возраст респондента больше среднего, а FALSE – в остальных случаях. Теперь этот логический вектор мы используем для выбора строк в исходном датафрейме:

Наконец, тут же мы можем вытащить нужные колонки, по номеру колонки или ее названию:

Эту же задачу можно выполнить другими способами:

В большинстве случаев подходят сразу несколько способов – тем не менее, стоит овладеть ими всеми. Чем богаче ваш арсенал инструментов работы в R, тем легче вам обрабатывать свои данные: возможность сделать одно и то же действие добавляет вам гибкости, потому что разные способы будут более или менее подходящими в разных ситуациях.

Датафреймы удобно просматривать в RStudio. Для это нужно написать команду View(df) или же просто нажать на названии нужной переменной из списка вверху справа (там где Environment). Тогда увидите табличку, очень похожую на Excel и тому подобные программы для работы с таблицами. Там же есть и всякие возможности для фильтрации, сортировки и поиска 2 .

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

Датафреймы – это структура, которая будет встречаться вам чаще всего при работе с данными в R. С одной стороны, кажется, что она все равно довольно ограниченная: в каждой колонке должно быть одинаковое количество значений, внутри колонки только один тип данных. Но именно так обычно и представлены наши данные. Например, если вы загрузите результаты опроса Google Forms в виде таблицы, то каждая строчка будет респондентом, а каждая колонка – ответом на какой-то вопрос. Поэтому количество значений в каждой колонке будет одинаковым (хотя значения могут быть пропущенными), а каждая колонка – имеет свой тип. Например, год рождения – и это должна быть числовая колонка, с которой вы сможете делать все, что вы умеете делать с числовыми колонками. Например, посчитать возраст. Если в колонке с годом рождения оказалось что-то кроме чисел, то это повод для исследования данных

Это универсальный порядок: что в других языках программирования, что в линейной алгебре первый индекс – выбор строчек, второй индекс – выбор столбцов.↩︎

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

Should I use a data.frame or a matrix?

When should one use a data.frame , and when is it better to use a matrix ?

Both keep data in a rectangular format, so sometimes it’s unclear.

Are there any general rules of thumb for when to use which data type?

MichaelChirico's user avatar

6 Answers 6

Part of the answer is contained already in your question: You use data frames if columns (variables) can be expected to be of different types (numeric/character/logical etc.). Matrices are for data of the same type.

Consequently, the choice matrix/data.frame is only problematic if you have data of the same type.

The answer depends on what you are going to do with the data in data.frame/matrix. If it is going to be passed to other functions then the expected type of the arguments of these functions determine the choice.

Matrices are more memory efficient:

Matrices are a necessity if you plan to do any linear algebra-type of operations.

Data frames are more convenient if you frequently refer to its columns by name (via the compact $ operator).

Data frames are also IMHO better for reporting (printing) tabular information as you can apply formatting to each column separately.

Something not mentioned by @Michal is that not only is a matrix smaller than the equivalent data frame, using matrices can make your code far more efficient than using data frames, often considerably so. That is one reason why internally, a lot of R functions will coerce to matrices data that are in data frames.

Data frames are often far more convenient; one doesn’t always have solely atomic chunks of data lying around.

Note that you can have a character matrix; you don’t just have to have numeric data to build a matrix in R.

In converting a data frame to a matrix, note that there is a data.matrix() function, which handles factors appropriately by converting them to numeric values based on the internal levels. Coercing via as.matrix() will result in a character matrix if any of the factor labels is non-numeric. Compare:

I nearly always use a data frame for my data analysis tasks as I often have more than just numeric variables. When I code functions for packages, I almost always coerce to matrix and then format the results back out as a data frame. This is because data frames are convenient.

@Michal: Matrices aren’t really more memory efficient:

. unless you have a large number of columns:

MichaelChirico's user avatar

The matrix is actually a vector with additional methods. while data.frame is a list. The difference is down to vector vs list. for computation efficiency, stick with matrix. Using data.frame if you have to.

I cannot stress out more the efficiency difference between the two! While it is true that DFs are more convenient in some especially data analysis cases, they also allow heterogeneous data, and some libraries accept them only, these all is really secondary unless you write a one-time code for a specific task.

Let me give you an example. There was a function that would calculate the 2D path of the MCMC method. Basically, this means we take an initial point (x,y), and iterate a certain algorithm to find a new point (x,y) at each step, constructing this way the whole path. The algorithm involves calculating a quite complex function and the generation of some random variable at each iteration, so when it run for 12 seconds I thought it is fine given how much stuff it does at each step. That being said, the function collected all points in the constructed path together with the value of an objective function in a 3-column data.frame. So, 3 columns is not that large, and the number of steps was also more than reasonable 10,000 (in this kind of problems paths of length 1,000,000 are typical, so 10,000 is nothing). So, I thought a DF 10,000×3 is definitely not an issue. The reason a DF was used is simple. After calling the function, ggplot() was called to draw the resulting (x,y)-path. And ggplot() does not accept a matrix.

Then, at some point out of curiosity I decided to change the function to collect the path in a matrix. Gladly the syntax of DFs and matrices is similar, all I did was to change the line specifying df as a data.frame to one initializing it as a matrix. Here I need also to mention that in the initial code the DF was initialized to have the final size, so later in the code of the function only new values were recorded into already allocated spaces, and there was no overhead of adding new rows to the DF. This makes the comparison even more fair, and it also made my job simpler as I did not need to rewrite anything further in the function. Just one line change from the initial allocation of a data.frame of the required size to a matrix of the same size. To adapt the new version of the function to ggplot(), I converted the now returned matrix to a data.frame to use in ggplot().

After I rerun the code I could not believe the result. The code run in a fraction of a second! Instead of about 12 seconds. And again, the function during the 10,000 iterations only read and wrote values to already allocated spaces in a DF (and now in a matrix). And this difference is also for the reasonable (or rather small) size 10000×3.

So, if your only reason to use a DF is to make it compatible with a library function such as ggplot(), you can always convert it to a DF at the last moment — work with matrices as far as you feel convenient. If on the other hand there is a more substantial reason to use a DF, such as you use some data analysis package that would require otherwise constant transforming from matrices to DFs and back, or you do not do any intensive calculations yourself and only use standard packages (many of them actually internally transform a DF to a matrix, do their job, and then transform the result back — so they do all efficiency work for you), or do a one-time job so you do not care and feel more comfortable with DFs, then you should not worry about efficiency.

Or a different more practical rule: if you have a question such as in the OP, use matrices, so you would use DFs only when you do not have such a question (because you already know you have to use DFs, or because you do not really care as the code is one-time etc.).

Matrices and Data Frames

In this lesson, we’ll cover matrices and data frames. Both represent ‘rectangular’ data types, meaning that they are used to store tabular data, with rows and columns.

The main difference, as you’ll see, is that matrices can only contain a single class of data, while data frames can consist of many different classes of data.

Let’s create a vector containing the numbers 1 through 20 using the : operator. Store the result in a variable called my_vector.

View the contents of the vector you just created.

The dim() function tells us the ‘dimensions’ of an object. What happens if we do dim(my_vector)? Give it a try.

Clearly, that’s not very helpful! Since my_vector is a vector, it doesn’t have a dim attribute (so it’s just NULL), but we can find its length using the length() function. Try that now.

Ah! That’s what we wanted. But, what happens if we give my_vector a dim attribute? Let’s give it a try. Type dim(my_vector) <- c(4, 5).

It’s okay if that last command seemed a little strange to you. It should! The dim() function allows you to get OR set the dim attribute for an R object. In this case, we assigned the value c(4, 5) to the dim attribute of my_vector.

Use dim(my_vector) to confirm that we’ve set the dim attribute correctly.

Another way to see this is by calling the attributes() function on my_vector. Try it now.

Just like in math class, when dealing with a 2-dimensional object (think rectangular table), the first number is the number of rows and the second is the number of columns. Therefore, we just gave my_vector 4 rows and 5 columns.

But, wait! That doesn’t sound like a vector any more. Well, it’s not. Now it’s a matrix. View the contents of my_vector now to see what it looks like.

Now, let’s confirm it’s actually a matrix by using the class() function. Type class(my_vector) to see what I mean.

Sure enough, my_vector is now a matrix. We should store it in a new variable that helps us remember what it is. Store the value of my_vector in a new variable called my_matrix.

The example that we’ve used so far was meant to illustrate the point that a matrix is simply an atomic vector with a dimension attribute. A more direct method of creating the same matrix uses the matrix() function.

Bring up the help file for the matrix() function now using the ? function.

Now, look at the documentation for the matrix function and see if you can figure out how to create a matrix containing the same numbers (1-20) and dimensions (4 rows, 5 columns) by calling the matrix() function. Store the result in a variable called my_matrix2.

Finally, let’s confirm that my_matrix and my_matrix2 are actually identical. The identical() function will tell us if its first two arguments are the same. Try it out.

Now, imagine that the numbers in our table represent some measurements from a clinical experiment, where each row represents one patient and each column represents one variable for which measurements were taken.

We may want to label the rows, so that we know which numbers belong to each patient in the experiment. One way to do this is to add a column to the matrix, which contains the names of all four people.

Let’s start by creating a character vector containing the names of our patients – Bill, Gina, Kelly, and Sean. Remember that double quotes tell R that something is a character string. Store the result in a variable called patients.

Now we’ll use the cbind() function to ‘combine columns’. Don’t worry about storing the result in a new variable. Just call cbind() with two arguments – the patients vector and my_matrix.

Something is fishy about our result! It appears that combining the character vector with our matrix of numbers caused everything to be enclosed in double quotes. This means we’re left with a matrix of character strings, which is no good.

If you remember back to the beginning of this lesson, I told you that matrices can only contain ONE class of data. Therefore, when we tried to combine a character vector with a numeric matrix, R was forced to ‘coerce’ the numbers to characters, hence the double quotes.

This is called ‘implicit coercion’, because we didn’t ask for it. It just happened. But why didn’t R just convert the names of our patients to numbers? I’ll let you ponder that question on your own.

So, we’re still left with the question of how to include the names of our patients in the table without destroying the integrity of our numeric data. Try the following – my_data <- data.frame(patients, my_matrix)

Now view the contents of my_data to see what we’ve come up with.

It looks like the data.frame() function allowed us to store our character vector of names right alongside our matrix of numbers. That’s exactly what we were hoping for!

Behind the scenes, the data.frame() function takes any number of arguments and returns a single object of class data.frame that is composed of the original objects.

Let’s confirm this by calling the class() function on our newly created data frame.

It’s also possible to assign names to the individual rows and columns of a data frame, which presents another possible way of determining which row of values in our table belongs to each patient.

However, since we’ve already solved that problem, let’s solve a different problem by assigning names to the columns of our data frame so that we know what type of measurement each column represents.

Since we have six columns (including patient names), we’ll need to first create a vector containing one element for each column. Create a character vector called cnames that contains the following values (in order) – “patient”, “age”, “weight”, “bp”, “rating”, “test”.

Now, use the colnames() function to set the colnames attribute for our data frame. This is similar to the way we used the dim() function earlier in this lesson.

Let’s see if that got the job done. Print the contents of my_data.

In this lesson, you learned the basics of working with two very important and common data structures – matrices and data frames. There’s much more to learn and we’ll be covering more advanced topics, particularly with respect to data frames, in future lessons.

Please submit the log of this lesson to Google Forms so that Simon may evaluate your progress.

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

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