這是介紹數據框處理的最后一篇文章。
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()
函數來創建TRUE
和FALSE
的向量來充當過濾器:
> 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 Field
是grassland
,第二個值必須為Twinspan
,因為Silwood Bottom
是arable
,依此類推。 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 platanoides
和Conyza sumatrensis
),因為沒有它們的開花時間數據。從開花數據庫中排除了三行(Chamerion angustifolium
,Conyza bilbaoana
和Brassica 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.x
和by.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 Smith
和Robert 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.pH
和Worm.density
,索引下標分別為2
,3
,5
和7
):
> 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
參數也必須是一個列表。 以下是Vegetation
和Damp
交叉分類的匯總結果:
> 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學堂”,我將定期更新相關文章。