R語言初級教程(21): 數據框(下篇)

這是介紹數據框處理的最后一篇文章。

10. 刪掉重復的行

有時,數據框會包含重復的行,其中所有變量在兩行或更多行中具有完全相同的值。 來看個簡單的示例:

> var1 <- c(1,2,3,4,3,6,1)
> var2 <- c(2,2,2,4,2,1,2)
> var3 <- c(3,2,1,2,1,2,3)
> var4 <- c(1,1,1,1,1,5,2)
> dups <- data.frame(var1, var2, var3, var4)
> dups
  var1 var2 var3 var4
1    1    2    1    1
2    2    2    1    1
3    3    2    1    1
4    4    4    1    1
5    3    2    1    1
6    6    1    5    5
7    1    2    2    2

注意,第5行與第3行完全相同。要刪除所有重復行,可以使用unique()函數:

> unique(dups)
  var1 var2 var3 var4
1    1    2    1    1
2    2    2    1    1
3    3    2    1    1
4    4    4    1    1
6    6    1    5    5
7    1    2    2    2

注意:新數據框中的行名與原始數據行中的行名相同,可以發現通過unique()函數刪除第5行了。

要查看數據框中重復的行(如果有的話),使用duplicated()函數來創建TRUEFALSE的向量來充當過濾器:

> dups[duplicated(dups), ]
  var1 var2 var3 var4
5    3    2    1    1

11. 處理日期變量

經常會碰到數據框中有日期變量,比如:

> nums <- read.table("C:/data/sortdata.txt", header=T)
> attach(nums)
> head(nums)
     name       date   response treatment
1  albert 25/08/2003 0.05963704         A
2     ann 21/05/2003 1.46555993         A
3    john 12/10/2003 1.59406539         B
4     ian 02/12/2003 2.09505949         A
5 michael 18/10/2003 2.38330748         B
6     ann 02/07/2003 2.86983693         B

想通過日期(date變量)對行進行排序。 按照之前的方法,排序無法按我們希望的方式進行:

> nums[order(date), ]
        name       date    response treatment
6        ann 02/07/2003  2.86983693         B
4        ian 02/12/2003  2.09505949         A
8      james 05/06/2003  4.90041370         A
13       ian 06/05/2003 39.97237868         A
9    william 11/06/2003  6.54439283         A
11 elizabeth 12/05/2003 39.39536726         B
3       john 12/10/2003  1.59406539         B
12   michael 14/06/2003 39.56900878         A
10    albert 14/07/2003 39.19746613         A
17   heather 14/11/2003 41.81821146         B
14      rose 15/05/2003 39.98892034         A
16  georgina 16/08/2003 40.81249037         A
5    michael 18/10/2003  2.38330748         B
2        ann 21/05/2003  1.46555993         A
15  georgina 24/05/2003 40.35117518         B
1     albert 25/08/2003  0.05963704         A
7   georgina 27/09/2003  3.37154802         B

這是因為用于描述日期的格式是一個字符串(首先是日,然后是月,最后是年),所以數據框是按首字母順序排序的,而不是按日期順序排序。為了按日期排序,我們首先需要使用strptime()函數將變量轉換為我們希望的日期時間格式(關于日期的處理,后期文章會詳細介紹):

> dates <- strptime(date, format="%d/%m/%Y")
> dates
 [1] "2003-08-25 CST" "2003-05-21 CST" "2003-10-12 CST" "2003-12-02 CST" "2003-10-18 CST"
 [6] "2003-07-02 CST" "2003-09-27 CST" "2003-06-05 CST" "2003-06-11 CST" "2003-07-14 CST"
[11] "2003-05-12 CST" "2003-06-14 CST" "2003-05-06 CST" "2003-05-15 CST" "2003-05-24 CST"
[16] "2003-08-16 CST" "2003-11-14 CST"

請注意:strptime()是如何生成日期對象的:首先是年份,然后是連字符-,然后是月份,然后是連字符-,然后是日,最后是字符串CST。正是我們所需的序列。將這個新變量綁定到數據框,如下所示:

nums <- cbind(nums, dates)

既然新日期變量的格式是正確的,日期就可以正確排序了:

> nums[order(dates), ]
        name       date    response treatment      dates
13       ian 06/05/2003 39.97237868         A 2003-05-06
11 elizabeth 12/05/2003 39.39536726         B 2003-05-12
14      rose 15/05/2003 39.98892034         A 2003-05-15
2        ann 21/05/2003  1.46555993         A 2003-05-21
15  georgina 24/05/2003 40.35117518         B 2003-05-24
8      james 05/06/2003  4.90041370         A 2003-06-05
9    william 11/06/2003  6.54439283         A 2003-06-11
12   michael 14/06/2003 39.56900878         A 2003-06-14
6        ann 02/07/2003  2.86983693         B 2003-07-02
10    albert 14/07/2003 39.19746613         A 2003-07-14
16  georgina 16/08/2003 40.81249037         A 2003-08-16
1     albert 25/08/2003  0.05963704         A 2003-08-25
7   georgina 27/09/2003  3.37154802         B 2003-09-27
3       john 12/10/2003  1.59406539         B 2003-10-12
5    michael 18/10/2003  2.38330748         B 2003-10-18
17   heather 14/11/2003 41.81821146         B 2003-11-14
4        ian 02/12/2003  2.09505949         A 2003-12-02

12. 使用match函數

上面的worms數據框包含五種不同植被類型的田地:

> worms <- read.table("C:/data/worms.txt",header=T)
> unique(worms$Vegetation)
[1] Grassland Arable    Meadow    Scrub     Orchard  
Levels: Arable Grassland Meadow Orchard Scrub
> worms
          Field.Name Area Slope Vegetation Soil.pH  Damp Worm.density
1        Nashs.Field  3.6    11  Grassland     4.1 FALSE            4
2     Silwood.Bottom  5.1     2     Arable     5.2 FALSE            7
3      Nursery.Field  2.8     3  Grassland     4.3 FALSE            2
4        Rush.Meadow  2.4     5     Meadow     4.9  TRUE            5
5    Gunness.Thicket  3.8     0      Scrub     4.2 FALSE            6
6           Oak.Mead  3.1     2  Grassland     3.9 FALSE            2
7       Church.Field  3.5     3  Grassland     4.2 FALSE            3
8            Ashurst  2.1     0     Arable     4.8 FALSE            4
9        The.Orchard  1.9     0    Orchard     5.7 FALSE            9
10     Rookery.Slope  1.5     4  Grassland     5.0  TRUE            7
11       Garden.Wood  2.9    10      Scrub     5.2 FALSE            8
12      North.Gravel  3.3     1  Grassland     4.1 FALSE            1
13      South.Gravel  3.7     2  Grassland     4.0 FALSE            2
14 Observatory.Ridge  1.8     6  Grassland     3.8 FALSE            0
15        Pond.Field  4.1     0     Meadow     5.0  TRUE            6
16      Water.Meadow  3.9     0     Meadow     4.9  TRUE            8
17         Cheapside  2.2     8      Scrub     4.7  TRUE            4
18        Pound.Hill  4.4     2     Arable     4.5 FALSE            5
19        Gravel.Pit  2.9     1  Grassland     3.5 FALSE            1
20         Farm.Wood  0.8    10      Scrub     5.1  TRUE            3

我們想知道在20塊地中每個地都適合使用的除草劑(Herbicide)。 除草劑數據位于另外一個數據框中,其中包含針對更大范圍的植物類型推薦的除草劑:

> herbicides <- read.table("C:/data/herbicides.txt",header=T)
> herbicides
        Type Herbicide
1   Woodland  Fusilade
2    Conifer  Weedwipe
3     Arable  Twinspan
4       Hill  Weedwipe
5    Bracken  Fusilade
6      Scrub  Weedwipe
7  Grassland  Allclear
8      Chalk  Vanquish
9     Meadow  Propinol
10      Lawn  Vanquish
11   Orchard  Fusilade
12     Verge  Allclear

任務是創建一個長度為20的向量(對應于worms數據框的20行),并把這個向量作為列加入到worms數據框中。 第一個值必須為Allclear,因為Nash’s Fieldgrassland,第二個值必須為Twinspan,因為Silwood Bottomarable,依此類推。 match()函數的第一個參數是worms$Vegetation,match中的第二個參數是herbicides$Type。 該匹配結果作為下標向量,從herbicides$Herbicide中提取相關除草劑。如下所示:

> herbicides$Herbicide[match(worms$Vegetation, herbicides$Type)]
 [1] Allclear Twinspan Allclear Propinol Weedwipe Allclear Allclear Twinspan
 [9] Fusilade Allclear Weedwipe Allclear Allclear Allclear Propinol Propinol
[17] Weedwipe Twinspan Allclear Weedwipe
Levels: Allclear Fusilade Propinol Twinspan Vanquish Weedwipe

將此信息添加為worms數據框中的新列:

> worms$hb <- herbicides$Herbicide[match(worms$Vegetation,herbicides$Type)]
> worms
          Field.Name Area Slope Vegetation Soil.pH  Damp Worm.density       hb
1        Nashs.Field  3.6    11  Grassland     4.1 FALSE            4 Allclear
2     Silwood.Bottom  5.1     2     Arable     5.2 FALSE            7 Twinspan
3      Nursery.Field  2.8     3  Grassland     4.3 FALSE            2 Allclear
4        Rush.Meadow  2.4     5     Meadow     4.9  TRUE            5 Propinol
5    Gunness.Thicket  3.8     0      Scrub     4.2 FALSE            6 Weedwipe
6           Oak.Mead  3.1     2  Grassland     3.9 FALSE            2 Allclear
7       Church.Field  3.5     3  Grassland     4.2 FALSE            3 Allclear
8            Ashurst  2.1     0     Arable     4.8 FALSE            4 Twinspan
9        The.Orchard  1.9     0    Orchard     5.7 FALSE            9 Fusilade
10     Rookery.Slope  1.5     4  Grassland     5.0  TRUE            7 Allclear
11       Garden.Wood  2.9    10      Scrub     5.2 FALSE            8 Weedwipe
12      North.Gravel  3.3     1  Grassland     4.1 FALSE            1 Allclear
13      South.Gravel  3.7     2  Grassland     4.0 FALSE            2 Allclear
14 Observatory.Ridge  1.8     6  Grassland     3.8 FALSE            0 Allclear
15        Pond.Field  4.1     0     Meadow     5.0  TRUE            6 Propinol
16      Water.Meadow  3.9     0     Meadow     4.9  TRUE            8 Propinol
17         Cheapside  2.2     8      Scrub     4.7  TRUE            4 Weedwipe
18        Pound.Hill  4.4     2     Arable     4.5 FALSE            5 Twinspan
19        Gravel.Pit  2.9     1  Grassland     3.5 FALSE            1 Allclear
20         Farm.Wood  0.8    10      Scrub     5.1  TRUE            3 Weedwipe

13. 合并兩個數據框

假設我們有兩個數據框,第一個包含有關植物生命形式的信息,第二個包含開花時間的信息。我們要將這兩個數據框融合成一個單一的數據框,以顯示有關生命形式和開花時間的信息。兩個數據框都包含屬名(genus)和種名(species)的變量:

> (lifeforms <- read.table("C:/data/lifeforms.txt",header=T))
   Genus     species lifeform
1   Acer platanoides     tree
2   Acer    palmatum     tree
3  Ajuga     reptans     herb
4 Conyza sumatrensis   annual
5 Lamium       album     herb

> (flowering <- read.table("C:/data/fltimes.txt",header=T))
      Genus       species flowering
1      Acer   platanoides       May
2     Ajuga       reptans      June
3  Brassica         napus     April
4 Chamerion angustifolium      July
5    Conyza     bilbaoana    August
6    Lamium         album   January

因為在兩個數據框中至少有一個變量名是相同的(在這里,兩個變量名是相同的,即屬(Genus)和種(species)),所以我們可以使用所有merge命令中最簡單的一個:

> merge(flowering, lifeforms)
   Genus     species flowering lifeform
1   Acer platanoides       May     tree
2  Ajuga     reptans      June     herb
3 Lamium       album   January     herb

需要注意的是,合并的數據框僅包含在兩個數據框中均具有完整條目的那些行。 從lifeforms數據庫中排除了兩行(Acer platanoidesConyza sumatrensis),因為沒有它們的開花時間數據。從開花數據庫中排除了三行(Chamerion angustifolium,Conyza bilbaoanaBrassica napus),因為沒有針對它們的生命形式數據。

如果要包括所有物種,但不知道開花時間或生命形式時用缺失值NA代替,則使用all = T選項:

> (both <- merge(flowering,lifeforms,all=T))
      Genus       species flowering lifeform
1      Acer   platanoides       May     tree
2      Acer      palmatum      <NA>     tree
3     Ajuga       reptans      June     herb
4  Brassica         napus     April     <NA>
5 Chamerion angustifolium      July     <NA>
6    Conyza     bilbaoana    August     <NA>
7    Conyza   sumatrensis      <NA>   annual
8    Lamium         album   January     herb

通常會出現的一種復雜情況是,需要合并的變量在兩個數據框中具有不同的名稱。最簡單的解決方案通常是將需要合并的變量名換成一樣就行。還有另一個辦法,則需要在第一個數據框(通常稱為x數據框)和第二個數據框(通常稱為y數據框)在merge中使用by.xby.y選項。我們有第三個數據框,其中包含有關所有8個物種的種子重量的信息,但變量屬Genus稱為name1,變量種species稱為name2

> (seeds <- read.table("C:/data/seedwts.txt",header=T))
      name1         name2 seed
1      Acer   platanoides 32.0
2    Lamium         album 12.0
3     Ajuga       reptans  4.0
4 Chamerion angustifolium  1.5
5    Conyza     bilbaoana  0.5
6  Brassica         napus  7.0
7      Acer      palmatum 21.0
8    Conyza   sumatrensis  0.6
> merge(both,seeds,by.x=c("Genus","species"),by.y=c("name1","name2"))
      Genus       species flowering lifeform seed
1      Acer      palmatum      <NA>     tree 21.0
2      Acer   platanoides       May     tree 32.0
3     Ajuga       reptans      June     herb  4.0
4  Brassica         napus     April     <NA>  7.0
5 Chamerion angustifolium      July     <NA>  1.5
6    Conyza     bilbaoana    August     <NA>  0.5
7    Conyza   sumatrensis      <NA>   annual  0.6
8    Lamium         album   January     herb 12.0

請注意,合并后數據框中使用的變量名稱是x數據框中使用的名稱。

14. 添加數據框的行列統計( margins)

假設我們有一個按季節和按人員顯示的銷售額數據框:

> frame <- read.table("C:/data/sales.txt", header=T)
> frame
             name spring summer autumn winter
1      Jane.Smith     14     18     11     12
2    Robert.Jones     17     18     10     13
3     Dick.Rogers     12     16      9     14
4 William.Edwards     15     14     11     10
5     Janet.Jones     11     17     11     16

我們想在此數據框中添加margins,以顯示季節性均值相對于總體均值的偏離(在底部增加一行),以及人員均值的偏離(在右側增加一列)。最后,我們希望數據框主體中的銷售額可以用與整體均值的偏差來表示。

> people <- rowMeans(frame[,2:5])
> people <- people-mean(people)
> people
[1]  0.30  1.05 -0.70 -0.95  0.30

使用cbind()函數向數據框添加新列非常簡單:

> (new.frame <- cbind(frame, people))
             name spring summer autumn winter people
1      Jane.Smith     14     18     11     12   0.30
2    Robert.Jones     17     18     10     13   1.05
3     Dick.Rogers     12     16      9     14  -0.70
4 William.Edwards     15     14     11     10  -0.95
5     Janet.Jones     11     17     11     16   0.30

Robert Jones是效率最高的銷售人員(+1.05),William Edwards是效率最低的銷售人員(–0.95)。列平均值的計算方法與此類似:

> seasons <- colMeans(frame[ ,2:5])
> seasons <- seasons-mean(seasons)
> seasons
spring summer autumn winter 
  0.35   3.15  -3.05  -0.45

夏季銷售額最高(+3.15),秋季最低(–3.05)。但是現在有一個問題,因為只有四列,但是在新數據框中有六列,所以不能直接使用rbind()函數。處理這個問題最簡單的方法是復制新數據框的一行

new.row <- new.frame[1, ]

然后對其進行編輯,以包含所需的值:第一列中的標簽表示“季節性”,然后四列中的平均值,最后是總的均值零:

> new.row[1] <- "seasonal effects"
> new.row[2:5] <- seasons
> new.row[6] <- 0
> new.row
              name spring summer autumn winter people
1 seasonal effects   0.35   3.15  -3.05  -0.45      0

現在,我們可以使用rbind()函數將新行添加到擴展數據框的底部:

> (new.frame <- rbind(new.frame,new.row))
              name spring summer autumn winter people
1       Jane.Smith  14.00  18.00  11.00  12.00   0.30
2     Robert.Jones  17.00  18.00  10.00  13.00   1.05
3      Dick.Rogers  12.00  16.00   9.00  14.00  -0.70
4  William.Edwards  15.00  14.00  11.00  10.00  -0.95
5      Janet.Jones  11.00  17.00  11.00  16.00   0.30
6 seasonal effects   0.35   3.15  -3.05  -0.45   0.00

最后一個任務是用與每人每個季節的總體平均銷售額(總均值gm=13.45)的偏離來替換數據框new.frame[1:5, 2:5]中的銷售計數。我們需要使用unlist()函數來阻止R估計每一列的均值;然后創建一個長度為4的向量,其中包含重復的均值(每列銷售額一個)。最后,我們使用sweep()函數將每個值減去總均值:

> gm <- mean(unlist(new.frame[1:5, 2:5]))
> gm <- rep(gm, 4)
> new.frame[1:5, 2:5] <- sweep(new.frame[1:5,2:5], 2, gm)
> new.frame
              name spring summer autumn winter people
1       Jane.Smith   0.55   4.55  -2.45  -1.45   0.30
2     Robert.Jones   3.55   4.55  -3.45  -0.45   1.05
3      Dick.Rogers  -1.45   2.55  -4.45   0.55  -0.70
4  William.Edwards   1.55   0.55  -2.45  -3.45  -0.95
5      Janet.Jones  -2.45   3.55  -2.45   2.55   0.30
6 seasonal effects   0.35   3.15  -3.05  -0.45   0.00

為了完成表格,我們要把總平均值放在右下角:

> new.frame[6,6] <- gm[1]
> new.frame
              name spring summer autumn winter people
1       Jane.Smith   0.55   4.55  -2.45  -1.45   0.30
2     Robert.Jones   3.55   4.55  -3.45  -0.45   1.05
3      Dick.Rogers  -1.45   2.55  -4.45   0.55  -0.70
4  William.Edwards   1.55   0.55  -2.45  -3.45  -0.95
5      Janet.Jones  -2.45   3.55  -2.45   2.55   0.30
6 seasonal effects   0.35   3.15  -3.05  -0.45  13.45

在夏季,Jane SmithRobert Jones表現最好,銷售量比夏季的整體平均水平高出4.55

15. 用aggregate匯總數據框

除了使用summary()函數來總結數據框內容外,我們也可以使用aggregate()函數。下面還是以worms數據框為例:

> worms <- read.table("C:/data/worms.txt",header=T)
> attach(worms)
> worms
          Field.Name Area Slope Vegetation Soil.pH  Damp Worm.density
1        Nashs.Field  3.6    11  Grassland     4.1 FALSE            4
2     Silwood.Bottom  5.1     2     Arable     5.2 FALSE            7
3      Nursery.Field  2.8     3  Grassland     4.3 FALSE            2
4        Rush.Meadow  2.4     5     Meadow     4.9  TRUE            5
5    Gunness.Thicket  3.8     0      Scrub     4.2 FALSE            6
6           Oak.Mead  3.1     2  Grassland     3.9 FALSE            2
7       Church.Field  3.5     3  Grassland     4.2 FALSE            3
8            Ashurst  2.1     0     Arable     4.8 FALSE            4
9        The.Orchard  1.9     0    Orchard     5.7 FALSE            9
10     Rookery.Slope  1.5     4  Grassland     5.0  TRUE            7
11       Garden.Wood  2.9    10      Scrub     5.2 FALSE            8
12      North.Gravel  3.3     1  Grassland     4.1 FALSE            1
13      South.Gravel  3.7     2  Grassland     4.0 FALSE            2
14 Observatory.Ridge  1.8     6  Grassland     3.8 FALSE            0
15        Pond.Field  4.1     0     Meadow     5.0  TRUE            6
16      Water.Meadow  3.9     0     Meadow     4.9  TRUE            8
17         Cheapside  2.2     8      Scrub     4.7  TRUE            4
18        Pound.Hill  4.4     2     Arable     4.5 FALSE            5
19        Gravel.Pit  2.9     1  Grassland     3.5 FALSE            1
20         Farm.Wood  0.8    10      Scrub     5.1  TRUE            3

aggregate()函數與tapply()函數類似,用于將一個函數(在本例中為mean)應用到指定的類別變量(在本例中為Vegetation)的各級別上,作用于指定變量(Area,Slope,Soil.pHWorm.density,索引下標分別為2,3,57):

> aggregate(worms[ ,c(2,3,5,7)], by=list(veg=Vegetation), mean)
        veg     Area    Slope  Soil.pH Worm.density
1    Arable 3.866667 1.333333 4.833333     5.333333
2 Grassland 2.911111 3.666667 4.100000     2.444444
3    Meadow 3.466667 1.666667 4.933333     6.333333
4   Orchard 1.900000 0.000000 5.700000     9.000000
5     Scrub 2.425000 7.000000 4.800000     5.250000

即使上例只有一個分類因子,by參數也必須是一個列表。 以下是VegetationDamp交叉分類的匯總結果:

> aggregate(worms[ ,c(2,3,5,7)], by=list(veg=Vegetation, d=Damp), mean)
        veg     d     Area    Slope  Soil.pH Worm.density
1    Arable FALSE 3.866667 1.333333 4.833333     5.333333
2 Grassland FALSE 3.087500 3.625000 3.987500     1.875000
3   Orchard FALSE 1.900000 0.000000 5.700000     9.000000
4     Scrub FALSE 3.350000 5.000000 4.700000     7.000000
5 Grassland  TRUE 1.500000 4.000000 5.000000     7.000000
6    Meadow  TRUE 3.466667 1.666667 4.933333     6.333333
7     Scrub  TRUE 1.500000 9.000000 4.900000     3.500000

今天的內容就到此為止,所有關于數據框的內容都介紹完了,希望對大家有點幫助。


感謝您的閱讀!想了解更多有關技巧,請關注我的微信公眾號“R語言和Python學堂”,我將定期更新相關文章。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。