9  Tables

表(table)是一种面向列或者列成表格的数据,这些数据经常以列的形式存储为文本格式或者表格格式。表包含多行和面向列的变量。表中的各变量可以具有不同的数据类型,也可以有不同的尺寸,只是各变量必须要具有相同的行数。例如用户可以使用表来存储实验数据,列用来表示不同的变量,行用来表示每次观测所得到的数值。

【例3-59】  表的创建。

本例将为读者演示怎样创建一个表。表是一种在一个容器内收集异构数据、元数据属性和变量单位等信息的数据类型。

MATLAB提供了一个名为'patients.dat'的测试数据。测试数据中是以逗号为分隔符的文本文件,包含了100位不同病人的信息。下面是该文件的前六行。

LastName,Gender,Age,Location,Height,Weight,Smoker,Systolic,Diastolic,SelfAssessedHealthStatus

Smith,Male,38,County General Hospital,71,176,1,124,93,Excellent

Johnson,Male,43,VA Hospital,69,163,0,109,77,Fair

Williams,Female,38,St. Mary's Medical Center,64,131,0,125,83,Good

Jones,Female,40,VA Hospital,67,133,0,117,75,Fair

Brown,Female,49,County General Hospital,64,119,0,122,80,Good

从文件内容可以看出,第一行包含了每列的名称,中间由逗号隔开。

使用readtable函数通过默认设置创建表。用户还可以使用readtable函数将Excel表格转换为表。readtable函数可以处理具有分隔符的后缀为txt、dat或csv的文本文件,也可以处理后缀为xls和xlsx的表格文件。

>> T = readtable('patients.dat');

查看表的尺寸信息:

>> size(T)

100    10

从结果可以看出,表T包含了10个变量100行数据。

通过以下命令可以查看表T的前5行数据和前5个变量:

>> T(1:5,1:5)

LastName      Gender     Age             Location              Height

__________    ________    ___    ___________________________    ______

'Smith'       'Male'      38     'County General Hospital'      71

'Johnson'     'Male'      43     'VA Hospital'                  69

'Williams'    'Female'    38     'St. Mary's Medical Center'    64

'Jones'       'Female'    40     'VA Hospital'                  67

'Brown'       'Female'    49     'County General Hospital'      64

默认情况下,readtable函数用第一行来作为变量名。如果第一行不包含变量名,那么用户可以使用T = readtable(filename,'ReadVariableNames',false)来改变默认设置。

此外用户还可以由workspace中的变量来创建表,具体的操作请读者查阅相关的帮助文档。

使用summary函数可以查看表的数据类型、描述、单位和其他统计信息,例如:

>> format compact                  %  以紧凑格式显示结果,以节约篇幅

>> summary(T)

LastName: 100x1 cell string

Gender: 100x1 cell string

Age: 100x1 double

min       25

median    39

max       50

Location: 100x1 cell string

Height: 100x1 double

min       60

median    67

max       72

Weight: 100x1 double

min         111

median    142.5

max         202

Smoker: 100x1 double

min       0

median    0

max       1

Systolic: 100x1 double

min       109

median    122

max       138

Diastolic: 100x1 double

min         68

median    81.5

max         99

SelfAssessedHealthStatus: 100x1 cell string

从结果中可以看出,表中的数据是多种格式的。其中LastName、Gender、Location和 SelfAssessedHealthStatus是字符串元胞数组,其他变量是数值格式。

将显示格式设置成默认格式。

>> format

表可以像普通数值矩阵那样通过小括号加下标来进行寻访。除了数值和逻辑型下标之外,用户还可以使用变量名和行名来作为下标。例如本例中可以使用LastName作为行名,然后将这一列数据删除。

>> T.Properties.RowNames = T.LastName;

>> T.LastName = [];

>> size(T)                      %  查看当前表T的尺寸

100     9

表T中包含了100行和9个变量。行名和变量名都不属于表的尺寸范畴。显示表的前五行和后4列来对此进行验证:

>> T(1:5,6:9)

Smoker    Systolic    Diastolic    SelfAssessedHealthStatus

______    ________    _________    ________________________

Smith       1         124         93           'Excellent'

Johnson     0         109         77           'Fair'

Williams    0         125         83           'Good'

Jones       0         117         75           'Fair'

Brown       0         122         80           'Good'

现在文本中的第一列,LastName,已经成为了行名。行名和变量名都是表的属性。用户可以通过T.Properties.RowNames来对已有表的行名进行添加或修改。

如果用户想将Systolic和Diastolic两个变量合并成为一个blood pressure变量,那么可以通过以下命令来实现:

>> T.BloodPressure = [T.Systolic T.Diastolic];

>> T(:,{'Systolic' 'Diastolic'}) = [];

>> size(T)               %  查看此时表的尺寸

100     8

从结果可以看到表T博阿含有100行8个变量。行名和变量名是表的属性,是不包含在尺寸中的。即使BloodPressure包含了两列,但依然是按照一个变量来统计的。

查看表的前5行和后4个变量:

>> T(1:5,5:8)

Weight    Smoker    SelfAssessedHealthStatus     BloodPressure

______    ______    ________________________    _______________

Smith       176       1         'Excellent'                 124          93

Johnson     163       0         'Fair'                      109          77

Williams    131       0         'Good'                      125          83

Jones       133       0         'Fair'                      117          75

Brown       119       0         'Good'                      122          80

从结果可以看出,刚才新合并的变量BloodPressure包含了两列数据,是表的最后一个变量。

基于已有变量(身高和体重)用户可以创建新的变量BMI,也就是体重指数。然后还可以添加变量的单位和描述等属性。

>> T.BMI = (T.Weight*0.)./(T.Height*0.0254).^2;

>> T.Properties.VariableUnits{'BMI'} = 'kg/m^2';

>> T.Properties.VariableDescriptions{'BMI'} = 'Body Mass Index';

>> size(T)           %  查看当前表的尺寸

100     9

现在表T包含了100行9个变量,比之前增加了一个变量BMI。现在查看一下前5行后4个变量所对应的数据:

>> T(1:5,6:9)

Smoker    SelfAssessedHealthStatus     BloodPressure      BMI

______    ________________________    _______________    ______

Smith       1         'Excellent'                 124          93    24.547

Johnson     0         'Fair'                      109          77    24.071

Williams    0         'Good'                      125          83    22.486

Jones       0         'Fair'                      117          75    20.831

Brown       0         'Good'                      122          80    20.426

【例3-60】  表中添加和删除行。

首先载入上例中的病人测试数据,并且创建表T。

>> load patients

>> T = table(LastName,Gender,Age,Height,Weight,Smoker,Systolic,Diastolic);

>> size(T)                 %  查看表的尺寸

100     8

从结果可以看出,表T有100行和8个变量。

创建一个以逗号为分隔符的其他病人的数据文件,其中包含了以下病人的数据:

LastName,Gender,Age,Height,Weight,Smoker,Systolic,Diastolic

Abbot,Female,31,65,156,1,128,85

Bailey,Female,38,68,130,0,113,81

Cho,Female,35,61,130,0,124,80

Daniels,Female,48,67,142,1,123,74

将此数据文件读入,并附在表T之后,合并为一个表。

>> T2 = readtable('morePatients.txt');

>> Tnew = [T;T2];

>> size(Tnew)

104     8

从结果可以看出表Tnew有104行数据。纵向合并两个表的话需要两个表具有相同的变量个数与变量名。如果变量名不同,那么用户可以通过命令T(end+1:end+4,:) = T2直接将另一个表中的数据作为新行赋值给原表。

如果用户想要将一个元胞数组中的数据作为新行与表合并,需要首先将元胞数组转换为表,然后再合并。

>> cellPatients = {'LastName','Gender','Age','Height','Weight',...

'Smoker','Systolic','Diastolic';

'Edwards','Male',42,70,158,0,116,83;

'Falk','Female',28,62,125,1,120,71};

>> T2 = cell2table(cellPatients(2:end,:));

>> T2.Properties.VariableNames = cellPatients(1,:);

>> Tnew = [Tnew;T2];

>> size(Tnew)          %  查看合并后的尺寸

106     8

用户还可以将结构数组中的数据与表合并。同样首先需要将结构数组转换为表,然后再和原表合并。

>> structPatients(1,1).LastName = 'George';

>> structPatients(1,1).Gender = 'Male';

>> structPatients(1,1).Age = 45;

>> structPatients(1,1).Height = 76;

>> structPatients(1,1).Weight = 182;

>> structPatients(1,1).Smoker = 1;

>> structPatients(1,1).Systolic = 132;

>> structPatients(1,1).Diastolic = 85;

>> structPatients(2,1).LastName = 'Hadley';

>> structPatients(2,1).Gender = 'Female';

>> structPatients(2,1).Age = 29;

>> structPatients(2,1).Height = 58;

>> structPatients(2,1).Weight = 120;

>> structPatients(2,1).Smoker = 0;

>> structPatients(2,1).Systolic = 112;

>> structPatients(2,1).Diastolic = 70;

>> Tnew = [Tnew;struct2table(structPatients)];

>> size(Tnew)              %  查看合并之后的表尺寸

108     8

使用unique函数可以删除在合并过程中产生的重复行数据。

>> Tnew = unique(Tnew);

>> size(Tnew)

106     8

从结果可以看出有2行重复的数据被删除了。

如果用户需要从表中删除18、20和21三行数据,方法如下:

>> Tnew([18,20,21],:) = [];

>> size(Tnew)

103     8

现在表中只包含103个病人的数据了。

如果用户想要通过行名来删除某行数据,那么需要先定义行名,然后删除作为行名的列,最后删除某行的数据。

>> Tnew.Properties.RowNames = Tnew.LastName;

>> Tnew.LastName = [];

>> Tnew('Smith',:) = [];

>> size(Tnew)

102     7

现在表中少了一个变量,也少了一个病人的数据。

假如用户需要删除符合某些条件的行数据,比如删除年龄小于30的病人数据,那么可以采用下面的方法:

>> toDelete = Tnew.Age<30;

>> Tnew(toDelete,:) = [];

>> size(Tnew)

85     7

现在表中又删除了17个病人的数据。