Powinienem użyć data.frame czy matrix?


152

Kiedy należy użyć a data.frame, a kiedy lepiej użyć matrix?

Oba przechowują dane w formacie prostokątnym, więc czasami jest to niejasne.

Czy są jakieś ogólne zasady określające, kiedy należy używać jakiego typu danych?


Często macierz może być lepiej dopasowana do określonego typu danych, ale jeśli pakiet, którego chcesz użyć do analizy tej macierzy, oczekuje ramki danych, zawsze będziesz musiał ją niepotrzebnie przekonwertować. Myślę, że nie ma sposobu, aby uniknąć pamiętania, który pakiet używa którego.
xApple,

Odpowiedzi:


176

Część odpowiedzi zawarta jest już w Twoim pytaniu: Używasz ramek danych, jeśli można oczekiwać, że kolumny (zmienne) będą różnego typu (numeryczne / znakowe / logiczne itp.). Macierze dotyczą danych tego samego typu.

W związku z tym wybór matrix / data.frame jest problematyczny tylko wtedy, gdy masz dane tego samego typu.

Odpowiedź zależy od tego, co zamierzasz zrobić z danymi w data.frame / matrix. Jeśli ma być przekazany do innych funkcji, to oczekiwany typ argumentów tych funkcji określa wybór.

Również:

Macierze są bardziej wydajne pod względem pamięci:

m = matrix(1:4, 2, 2)
d = as.data.frame(m)
object.size(m)
# 216 bytes
object.size(d)
# 792 bytes

Macierze są konieczne, jeśli planujesz wykonywać operacje typu algebry liniowej.

Ramki danych są wygodniejsze, jeśli często odwołujesz się do ich kolumn według nazwy (za pośrednictwem kompaktowego operatora $).

Ramki danych są również lepsze w IMHO do raportowania (drukowania) informacji tabelarycznych, ponieważ można zastosować formatowanie do każdej kolumny oddzielnie.


5
Jedną rzeczą, którą chciałbym dodać do tej odpowiedzi, jest to, że jeśli planujesz używać pakietu ggplot2 do tworzenia wykresów, ggplot2 działa tylko z data.frames, a nie z macierzami. Po prostu coś, o czym należy pamiętać!
Bajcz

77

Coś, o czym @Michal nie wspomniał, to fakt, że macierz jest nie tylko mniejsza niż odpowiadająca jej ramka danych, ale użycie macierzy może znacznie zwiększyć wydajność kodu niż użycie ramek danych, często znacznie. Jest to jeden z powodów, dla których wewnętrznie wiele funkcji języka R będzie przekształcać dane macierzy w ramkach danych.

Ramki danych są często znacznie wygodniejsze; nie zawsze można znaleźć tylko atomowe fragmenty danych.

Zauważ, że możesz mieć macierz znaków; nie musisz tylko mieć danych liczbowych, aby zbudować macierz w R.

Konwertując ramkę danych na macierz, należy zwrócić uwagę, że istnieje data.matrix()funkcja, która odpowiednio obsługuje czynniki, konwertując je na wartości liczbowe w oparciu o poziomy wewnętrzne. Wymuszanie za pośrednictwem as.matrix()spowoduje powstanie macierzy znaków, jeśli którakolwiek z etykiet czynników nie jest numeryczna. Porównać:

> head(as.matrix(data.frame(a = factor(letters), B = factor(LETTERS))))
     a   B  
[1,] "a" "A"
[2,] "b" "B"
[3,] "c" "C"
[4,] "d" "D"
[5,] "e" "E"
[6,] "f" "F"
> head(data.matrix(data.frame(a = factor(letters), B = factor(LETTERS))))
     a B
[1,] 1 1
[2,] 2 2
[3,] 3 3
[4,] 4 4
[5,] 5 5
[6,] 6 6

Prawie zawsze używam ramki danych do moich zadań analizy danych, ponieważ często mam więcej niż tylko zmienne numeryczne. Kiedy koduję funkcje dla pakietów, prawie zawsze używam macierzy, a następnie formatuję wyniki z powrotem jako ramkę danych. Dzieje się tak, ponieważ ramki danych są wygodne.


Zastanawiałem się też nad różnicą między data.matrix () a as.matrix (). Dzięki za wyjaśnienie ich i wskazówki dotyczące programowania.
mikrob

Dzięki za udostępnienie @Gavin Simpson! Czy mógłbyś przedstawić trochę więcej informacji na temat powrotu od 1 do 6 do af?
YJZ,

1
@YZhang Musiałbyś przechowywać etykiety dla każdego czynnika i logiczny wektor wskazujący, które kolumny macierzy były czynnikami. Wtedy konwersja tylko tych kolumn, które były czynnikami, z powrotem na czynniki z poprawnymi etykietami, byłaby stosunkowo prosta. Komentarze nie są dobrymi miejscami na kod, więc sprawdź, czy pytanie Q zostało już zadane i na które udzielono odpowiedzi, a jeśli nie, zadaj nowe pytanie.
Gavin Simpson,

47

@Michal: Matryce nie są tak naprawdę wydajniejsze pod względem pamięci:

m <- matrix(1:400000, 200000, 2)
d <- data.frame(m)
object.size(m)
# 1600200 bytes
object.size(d)
# 1600776 bytes

... chyba że masz dużą liczbę kolumn:

m <- matrix(1:400000, 2, 200000)
d <- data.frame(m)
object.size(m)
# 1600200 bytes
object.size(d)
# 22400568 bytes

argument dotyczący wydajności pamięci polega tak naprawdę na data.framesoferowaniu większej elastyczności w stosunku do typów kolumn. data.frame(a = rnorm(1e6), b = sample(letters, 1e6, TRUE))będzie znacznie mniejszy (6x według moich szybkich obliczeń) w pamięci niż matrixwersja z powodu przymusu typu.
MichaelChirico

9

Macierz jest w rzeczywistości wektorem z dodatkowymi metodami. podczas gdy data.frame to lista. Różnica sprowadza się do wektora a listy. dla wydajności obliczeń trzymaj się macierzy. Korzystanie z data.frame, jeśli musisz.


3
Hmm, macierz to wektor z wymiarami, nie widzę, gdzie przychodzą do tego metody?
Gavin Simpson

0

Macierze i ramki danych są prostokątnymi tablicami 2D i mogą być niejednorodne ze względu na wiersze i kolumny . Mają wspólne metody i właściwości, ale nie wszystkie.

Przykłady:

M <- list(3.14,TRUE,5L,c(2,3,5),"dog",1i)  # a list
dim(M) <- c(2,3)                           # set dimensions
print(M)                                   # print result

#      [,1]  [,2]      [,3]
# [1,] 3.14  5         "dog"
# [2,] TRUE  Numeric,3 0+1i

DF <- data.frame(M)                   # a data frame
print(DF)                             # print result

#      X1      X2   X3
#  1 3.14       5  dog
#  2 TRUE 2, 3, 5 0+1i

M <- matrix(c(1,1,1,1,2,3,1,3,6),3)   # a numeric matrix
DF <- data.frame(M)                   # a all numeric data frame

solve(M)                              # obtains inverse matrix
solve(DF)                             # obtains inverse matrix
det(M)                                # obtains determinant
det(DF)                               # error

0

Nie mogę bardziej podkreślić różnicy wydajności między nimi! Chociaż prawdą jest, że DF są wygodniejsze w niektórych przypadkach, szczególnie w przypadku analizy danych, dopuszczają również dane heterogeniczne, a niektóre biblioteki akceptują je tylko, to wszystko jest naprawdę drugorzędne, chyba że napiszesz jednorazowy kod do określonego zadania.

Dam ci przykład. Była funkcja, która obliczałaby ścieżkę 2D metody MCMC. Zasadniczo oznacza to, że bierzemy punkt początkowy (x, y) i iterujemy pewien algorytm, aby znaleźć nowy punkt (x, y) na każdym kroku, konstruując w ten sposób całą ścieżkę. Algorytm polega na obliczaniu dość złożonej funkcji i generowaniu pewnej zmiennej losowej w każdej iteracji, więc gdy działa przez 12 sekund, pomyślałem, że jest w porządku, biorąc pod uwagę, ile rzeczy robi na każdym kroku. To powiedziawszy, funkcja zebrała wszystkie punkty na skonstruowanej ścieżce wraz z wartością funkcji celu w 3-kolumnowej ramce data.frame. Tak więc 3 kolumny nie są tak duże, a liczba kroków była również większa niż rozsądne 10 000 (w tego rodzaju problemach typowe są ścieżki o długości 1 000 000, więc 10 000 to nic). Więc pomyślałem, że DF 10, 000x3 zdecydowanie nie stanowi problemu. Powód użycia DF jest prosty. Po wywołaniu funkcji została wywołana ggplot (), aby narysować wynikową (x, y) -path. A ggplot () nie akceptuje macierzy.

Następnie w pewnym momencie z ciekawości postanowiłem zmienić funkcję, aby zbierać ścieżkę w macierzy. Na szczęście składnia DF i macierzy jest podobna, wszystko, co zrobiłem, to zmienić linię określającą df jako ramkę danych na taką, która inicjuje ją jako macierz. W tym miejscu muszę również wspomnieć, że w początkowym kodzie DF został zainicjowany, aby mieć ostateczny rozmiar, więc później w kodzie funkcji zapisywane były tylko nowe wartości do już przydzielonych spacji i nie było narzutu dodawania nowych wierszy do DF. To sprawia, że ​​porównanie jest jeszcze bardziej sprawiedliwe, a także uprościło mi pracę, ponieważ nie musiałem nic więcej przepisywać w funkcji. Tylko jedna linia zmienia się od początkowego przydzielenia ramki danych o wymaganym rozmiarze do macierzy o tym samym rozmiarze. Aby dostosować nową wersję funkcji do ggplot (), przekonwertowałem zwróconą macierz na dane.

Po ponownym uruchomieniu kodu nie mogłem uwierzyć w wynik. Kod działa w ułamku sekundy! Zamiast około 12 sekund. I znowu, funkcja podczas 10 000 iteracji tylko odczytywała i zapisywała wartości w już przydzielonych przestrzeniach w DF (a teraz w macierzy). I ta różnica dotyczy również rozsądnego (lub raczej małego) rozmiaru 10000x3.

Tak więc, jeśli jedynym powodem używania DF jest uczynienie go kompatybilnym z funkcją biblioteczną, taką jak ggplot (), zawsze możesz przekonwertować go na DF w ostatniej chwili - pracuj z macierzami, o ile uważasz to za wygodne. Jeśli z drugiej strony istnieje bardziej istotny powód, aby używać DF, na przykład używasz pakietu do analizy danych, który wymagałby w innym przypadku ciągłego przekształcania z macierzy na DF iz powrotem, lub nie wykonujesz samodzielnie żadnych intensywnych obliczeń i używasz tylko standardowych pakiety (wiele z nich faktycznie wewnętrznie przekształca DF w macierz, wykonuje swoją pracę, a następnie przekształca wynik z powrotem - więc wykonują dla Ciebie całą pracę związaną z wydajnością) lub wykonuje jednorazową pracę, aby nie przejmować się i czuć wygodniej z DF, nie powinieneś martwić się o wydajność.

Albo inna, bardziej praktyczna zasada: jeśli masz pytanie takie jak w PO, użyj macierzy, więc użyjesz DF tylko wtedy, gdy nie masz takiego pytania (ponieważ już wiesz, że musisz używać DFs, lub ponieważ robisz nie obchodzi mnie to, ponieważ kod jest jednorazowy itp.).

Ale ogólnie rzecz biorąc, należy zawsze mieć na uwadze ten punkt wydajności jako priorytet.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.