R语言数据分析(五)
文章目录
前言
我们学习了数据导入、可视化以及数据转换的相关知识,那么在数据导入后要进行的就是数据的整理,这是一个将凌乱的数据整理成为整洁数据的过程。只有整洁的数据才能很好的进行数据转换分析以及可视化。在这一节的学习中我们将会使用一种称为tidy data的系统在R中组织数据。(注意加载tidyverse包)
一、什么是整洁的数据
数据的呈现方式多种多样,下面的示例展示了三种不同的方式整理的相同的数据。每个数据都用于显示一组数据,包括:英雄名称、版本、攻击力和防御力数值,但每个数据集组织这些值的方式不同:
# 这里是我自定义的table1、2、3,可以自己去自定义一些tibble顺便复习前面的知识了,其实在tidyverse中也有自己的table1、2、3存在,可以自行查看。
table1
#> # A tibble: 6 × 4
#> Hero Version ATK DEF
#> <chr> <dbl> <dbl> <dbl>
#> 1 LMY 1 999 666
#> 2 LMY 2 1000 999
#> 3 NEW 1 99 66
#> 4 NEW 2 100 99
#> 5 Z 1 5 1
#> 6 Z 2 5 0.5
table2
#> # A tibble: 12 × 4
#> Hero Version type value
#> <chr> <dbl> <chr> <dbl>
#> 1 LMY 1 ATK 999
#> 2 LMY 1 DEF 666
#> 3 LMY 2 ATK 1000
#> 4 LMY 2 DEF 999
#> 5 NEW 1 ATK 99
#> 6 NEW 1 DEF 100
#> 7 NEW 2 ATK 66
#> 8 NEW 2 DEF 99
#> 9 Z 1 ATK 5
#> 10 Z 1 DEF 5
#> 11 Z 2 ATK 1
#> 12 Z 2 DEF 0.5
table3
#> # A tibble: 6 × 3
#> Hero Version rate
#> <chr> <dbl> <dbl>
#> 1 LMY 1 1.5
#> 2 LMY 2 1.00
#> 3 NEW 1 0.99
#> 4 NEW 2 0.667
#> 5 Z 1 1
#> 6 Z 2 2
其中table1是在tidyverse中容易使用的形式,以为它是tidy的。
整洁的数据集需要遵循三个相互关联的规则:
-
每个变量都是一列,每一列都是一个变量。
-
每个观测值都是一行,每一行都是一个观测值。
-
每个值都是一个单元格,每个单元格都是一个值。
保持数据的整洁主要有两个优点:
-
一致的数据存储方式有一个普遍的优势。更易于学习工具,具有底层的一致性
-
变量放在列中有一个特定的优势,以为它很贴合R的矢量化性质。
tidyverse中所有软件包都旨在处理整洁的数据,下面是一些小示例:
table1 |>
mutate(rate = ATK / DEF)
#> # A tibble: 6 × 5
#> Hero Version ATK DEF rate
#> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 LMY 1 999 666 1.5
#> 2 LMY 2 1000 999 1.00
#> 3 NEW 1 99 66 1.5
#> 4 NEW 2 100 99 1.01
#> 5 Z 1 5 1 5
#> 6 Z 2 5 0.5 10
table1 |>
group_by(Version) |>
summarize(total_version = sum(ATK))
#> # A tibble: 2 × 2
#> Version total_version
#> <dbl> <dbl>
#> 1 1 1103
#> 2 2 1105
ggplot(table1, aes(x = Version, y = ATK)) +
geom_line(aes(group = Hero), color = "Blue") +
geom_point(aes(color = Hero, shape = Hero)) +
scale_x_continuous(breaks = c(1.0, 2.0))
二、延长数据
整洁数据的原则看起来十分简单,但是在大多数情况下,遇到的数据都是不整洁的。这就意味着大多数实际分析至少需要一点整理。
首先要弄清楚基础变量和观察值是什么。接下来,要将数据转化为整洁的形式,列包含变量,行包含观测值。
2.1 列名中的数据值
tidyverse中的billboard
数据集记录了2000年歌曲广告排名:
billboard
#> # A tibble: 317 × 79
#> artist track date.entered wk1 wk2 wk3 wk4 wk5 wk6 wk7 wk8
#> <chr> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 2 Pac Baby… 2000-02-26 87 82 72 77 87 94 99 NA
#> 2 2Ge+her The … 2000-09-02 91 87 92 NA NA NA NA NA
#> 3 3 Doors D… Kryp… 2000-04-08 81 70 68 67 66 57 54 53
#> 4 3 Doors D… Loser 2000-10-21 76 76 72 69 67 65 55 59
#> 5 504 Boyz Wobb… 2000-04-15 57 34 25 17 17 31 36 49
#> 6 98^0 Give… 2000-08-19 51 39 34 26 26 19 2 2
#> 7 A*Teens Danc… 2000-07-08 97 97 96 95 100 NA NA NA
#> 8 Aaliyah I Do… 2000-01-29 84 62 51 41 38 35 35 38
#> 9 Aaliyah Try … 2000-03-18 59 53 38 28 21 18 16 14
#> 10 Adams, Yo… Open… 2000-08-26 76 76 74 69 68 67 61 58
#> # ℹ 307 more rows
#> # ℹ 68 more variables: wk9 <dbl>, wk10 <dbl>, wk11 <dbl>, wk12 <dbl>,
#> # wk13 <dbl>, wk14 <dbl>, wk15 <dbl>, wk16 <dbl>, wk17 <dbl>, wk18 <dbl>,
#> # wk19 <dbl>, wk20 <dbl>, wk21 <dbl>, wk22 <dbl>, wk23 <dbl>, wk24 <dbl>,
#> # wk25 <dbl>, wk26 <dbl>, wk27 <dbl>, wk28 <dbl>, wk29 <dbl>, wk30 <dbl>,
#> # wk31 <dbl>, wk32 <dbl>, wk33 <dbl>, wk34 <dbl>, wk35 <dbl>, wk36 <dbl>,
#> # wk37 <dbl>, wk38 <dbl>, wk39 <dbl>, wk40 <dbl>, wk41 <dbl>, wk42 <dbl>, …
其中,每个观测值是一首歌,前三列是歌曲信息,之后有76列来描述歌曲每周的排名。这76列中,列名是一个变量(week),单元格值是另一个变量(week rank)。
为了整理这些数据,我们使用pivot_longer()
函数:
billboard |>
pivot_longer(
cols = starts_with("wk"),
names_to = "week",
values_to = "rank"
)
#> # A tibble: 24,092 × 5
#> artist track date.entered week rank
#> <chr> <chr> <date> <chr> <dbl>
#> 1 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk1 87
#> 2 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk2 82
#> 3 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk3 72
#> 4 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk4 77
#> 5 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk5 87
#> 6 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk6 94
#> 7 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk7 99
#> 8 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk8 NA
#> 9 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk9 NA
#> 10 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk10 NA
#> # ℹ 24,082 more rows
使用此函数需要注意:
-
cols
参数用于指定哪些列需要进行整合,这个参数使用的语法与select()
相同,因此可以使用!c(artist, track, data.entered)
或者starts_with("wk")
。 -
names_to
参数对存储在列名中的变量进行命名。 -
values_to
参数将存储在单元格中的变量进行命名。
我们注意到转化后的数据中有NA
值。比如数据的第8行就存在。这是因为这首歌在第8周并未进入前100名所导致的。那么这些NA
并不是真正的观察结果。因此可以使用参数values_drop_na = TRUE
来使得pivot_longer()
删除NA
。
现在数据就是整洁的了,不过我们可以通过使用mutate()
和readr::parse_number()
来将字符串变为数字,parse_number()
函数将从字符串提取第一个数字,忽略所有其他文本:
billboard_longer <- billboard |>
pivot_longer(
cols = starts_with("wk"),
names_to = "week",
values_to = "rank",
values_drop_na = TRUE
) |>
mutate(
week = parse_number(week)
)
billboard_longer
#> # A tibble: 5,307 × 5
#> artist track date.entered week rank
#> <chr> <chr> <date> <dbl> <dbl>
#> 1 2 Pac Baby Don't Cry (Keep... 2000-02-26 1 87
#> 2 2 Pac Baby Don't Cry (Keep... 2000-02-26 2 82
#> 3 2 Pac Baby Don't Cry (Keep... 2000-02-26 3 72
#> 4 2 Pac Baby Don't Cry (Keep... 2000-02-26 4 77
#> 5 2 Pac Baby Don't Cry (Keep... 2000-02-26 5 87
#> 6 2 Pac Baby Don't Cry (Keep... 2000-02-26 6 94
#> 7 2 Pac Baby Don't Cry (Keep... 2000-02-26 7 99
#> 8 2Ge+her The Hardest Part Of ... 2000-09-02 1 91
#> 9 2Ge+her The Hardest Part Of ... 2000-09-02 2 87
#> 10 2Ge+her The Hardest Part Of ... 2000-09-02 3 92
#> # ℹ 5,297 more rows
现在我们就可以对这样的结果进行很好的可视化了
billboard_longer |>
ggplot(aes(x = week, y = rank, group = track)) +
geom_line(alpha = 0.1) +
scale_y_reverse()
2.2 pivot_longer()
的处理原理
明白使用函数pivot_longer()
处理的原理很重要,这样我们就能够在需要使用这些功能的时候能更快的想到该函数。
假设我们有三个小朋友,记为A、B、C。两年分别测量了两次身高。我们建立一个tibble表格来记录数据:
df <- tribble(
~id, ~h1, ~h2,
"A", 110, 130,
"B", 100, 110,
"C", 80, 135
)
我们经过处理后得到以下数据集:
df |>
pivot_longer(
cols = h1:h2,
names_to = "year_h",
values_to = "value"
)
#> # A tibble: 6 × 3
#> id year_h value
#> <chr> <chr> <dbl>
#> 1 A h1 110
#> 2 A h2 130
#> 3 B h1 100
#> 4 B h2 110
#> 5 C h1 80
#> 6 C h2 135
函数如何工作的呢?逐列来看,我们发现了其工作模式。如下图所示:
-
已有的列重复了cols参数中列数的次数
-
cols参数中的列名重复了行数的次数
-
cols参数中列的值从上到下从左到右依次进行排列
2.3 列名中包含许多变量的情况
有时列名可能包含许多信息。比如,在who2
上可以看到WHO发布的关于结核病诊断的信息:
who2
#> # A tibble: 7,240 × 58
#> country year sp_m_014 sp_m_1524 sp_m_2534 sp_m_3544 sp_m_4554 sp_m_5564
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Afghanistan 1980 NA NA NA NA NA NA
#> 2 Afghanistan 1981 NA NA NA NA NA NA
#> 3 Afghanistan 1982 NA NA NA NA NA NA
#> 4 Afghanistan 1983 NA NA NA NA NA NA
#> 5 Afghanistan 1984 NA NA NA NA NA NA
#> 6 Afghanistan 1985 NA NA NA NA NA NA
#> 7 Afghanistan 1986 NA NA NA NA NA NA
#> 8 Afghanistan 1987 NA NA NA NA NA NA
#> 9 Afghanistan 1988 NA NA NA NA NA NA
#> 10 Afghanistan 1989 NA NA NA NA NA NA
#> # ℹ 7,230 more rows
#> # ℹ 50 more variables: sp_m_65 <dbl>, sp_f_014 <dbl>, sp_f_1524 <dbl>,
#> # sp_f_2534 <dbl>, sp_f_3544 <dbl>, sp_f_4554 <dbl>, sp_f_5564 <dbl>,
#> # sp_f_65 <dbl>, sn_m_014 <dbl>, sn_m_1524 <dbl>, sn_m_2534 <dbl>,
#> # sn_m_3544 <dbl>, sn_m_4554 <dbl>, sn_m_5564 <dbl>, sn_m_65 <dbl>,
#> # sn_f_014 <dbl>, sn_f_1524 <dbl>, sn_f_2534 <dbl>, sn_f_3544 <dbl>,
#> # sn_f_4554 <dbl>, sn_f_5564 <dbl>, sn_f_65 <dbl>, ep_m_014 <dbl>, …
除了country和year列外,剩下56列命名格式统一,由三部分组成分别是诊断方法、性别和年龄范围,用_
分隔。这种情况下记录了6条消息。我们可以使用pivot_longer
将其分开:
who2 |>
pivot_longer(
cols = !(country:year),
names_to = c("diagnosis","gender", "age"),
names_sep = "_",
values_to = "count"
)
#> # A tibble: 405,440 × 6
#> country year diagnosis gender age count
#> <chr> <dbl> <chr> <chr> <chr> <dbl>
#> 1 Afghanistan 1980 sp m 014 NA
#> 2 Afghanistan 1980 sp m 1524 NA
#> 3 Afghanistan 1980 sp m 2534 NA
#> 4 Afghanistan 1980 sp m 3544 NA
#> 5 Afghanistan 1980 sp m 4554 NA
#> 6 Afghanistan 1980 sp m 5564 NA
#> 7 Afghanistan 1980 sp m 65 NA
#> 8 Afghanistan 1980 sp f 014 NA
#> 9 Afghanistan 1980 sp f 1524 NA
#> 10 Afghanistan 1980 sp f 2534 NA
#> # ℹ 405,430 more rows
另外还有一种方法可以替代names_sep
就是names_pattern
,这需要用到正则表达式来提取变量,我们后续在逐步介绍。
从原理上来看,他是将原来names_to
参数使用的列排列完之后,又按照names_sep
的规则进行了分列。
2.4 列名同时包含数据和变量
有些更复杂的数据集,其列名同时包含了数据和变量。比如:
household
#> # A tibble: 5 × 5
#> family dob_child1 dob_child2 name_child1 name_child2
#> <int> <date> <date> <chr> <chr>
#> 1 1 1998-11-26 2000-01-29 Susan Jose
#> 2 2 1996-06-22 NA Mark <NA>
#> 3 3 2002-07-11 2004-04-05 Sam Seth
#> 4 4 2004-10-10 2009-08-27 Craig Khai
#> 5 5 2000-12-05 2005-02-28 Parker Gracie
这里记录了5个家庭数据,其中最多包含两个孩子的姓名及出生日期。这个数据集的列名中包含了两个变量的名称(name
和dob
),以及另一个变量的值(child
的值1
或2
)。此时想要解决这个问题,names_to
会使用特殊的".value"作为值的指示,而不是新列名,这样会覆盖掉values_to
的参数:
household |>
pivot_longer(
cols = !family,
names_to = c(".value", "child"),
names_sep = "_",
values_drop_na = TRUE
)
#> # A tibble: 9 × 4
#> family child dob name
#> <int> <chr> <date> <chr>
#> 1 1 child1 1998-11-26 Susan
#> 2 1 child2 2000-01-29 Jose
#> 3 2 child1 1996-06-22 Mark
#> 4 3 child1 2002-07-11 Sam
#> 5 3 child2 2004-04-05 Seth
#> 6 4 child1 2004-10-10 Craig
#> 7 4 child2 2009-08-27 Khai
#> 8 5 child1 2000-12-05 Parker
#> 9 5 child2 2005-02-28 Gracie
具体情况就像上面这样,一目了然。
三、扩宽数据
有时一个观测值的指标可能分布在多行中,这时我们就可以通过增加列和减少行的方法来扩宽数据集。使用到的函数是pivot_wider
。比如下面的数据集:
cms_patient_experience
#> # A tibble: 500 × 5
#> org_pac_id org_nm measure_cd measure_title prf_rate
#> <chr> <chr> <chr> <chr> <dbl>
#> 1 0446157747 USC CARE MEDICAL GROUP INC CAHPS_GRP… CAHPS for MI… 63
#> 2 0446157747 USC CARE MEDICAL GROUP INC CAHPS_GRP… CAHPS for MI… 87
#> 3 0446157747 USC CARE MEDICAL GROUP INC CAHPS_GRP… CAHPS for MI… 86
#> 4 0446157747 USC CARE MEDICAL GROUP INC CAHPS_GRP… CAHPS for MI… 57
#> 5 0446157747 USC CARE MEDICAL GROUP INC CAHPS_GRP… CAHPS for MI… 85
#> 6 0446157747 USC CARE MEDICAL GROUP INC CAHPS_GRP… CAHPS for MI… 24
#> 7 0446162697 ASSOCIATION OF UNIVERSITY PHYSI… CAHPS_GRP… CAHPS for MI… 59
#> 8 0446162697 ASSOCIATION OF UNIVERSITY PHYSI… CAHPS_GRP… CAHPS for MI… 85
#> 9 0446162697 ASSOCIATION OF UNIVERSITY PHYSI… CAHPS_GRP… CAHPS for MI… 83
#> 10 0446162697 ASSOCIATION OF UNIVERSITY PHYSI… CAHPS_GRP… CAHPS for MI… 63
#> # ℹ 490 more rows
该数据集研究的目标是一个组织,但是每个组织的数据被分布在了6行中,调查组织中的每个测量值各占一行。我们可以看到:
cms_patient_experience |>
distinct(measure_cd, measure_title)
#> # A tibble: 6 × 2
#> measure_cd measure_title
#> <chr> <chr>
#> 1 CAHPS_GRP_1 CAHPS for MIPS SSM: Getting Timely Care, Appointments, and Infor…
#> 2 CAHPS_GRP_2 CAHPS for MIPS SSM: How Well Providers Communicate
#> 3 CAHPS_GRP_3 CAHPS for MIPS SSM: Patient's Rating of Provider
#> 4 CAHPS_GRP_5 CAHPS for MIPS SSM: Health Promotion and Education
#> 5 CAHPS_GRP_8 CAHPS for MIPS SSM: Courteous and Helpful Office Staff
#> 6 CAHPS_GRP_12 CAHPS for MIPS SSM: Stewardship of Patient Resources
这两列都不是很好的变量名,其中measure_cd
并没有按时变量的含义,measure_tittle
也只是包含一个长句子。现在,让我们使用measure_cd列作为我们新列名的源。创建一个新的数据集:
cms_patient_experience |>
pivot_wider(
names_from = measure_cd,
values_from = prf_rate
)
#> # A tibble: 500 × 9
#> org_pac_id org_nm measure_title CAHPS_GRP_1 CAHPS_GRP_2 CAHPS_GRP_3
#> <chr> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 0446157747 USC CARE MEDICA… CAHPS for MI… 63 NA NA
#> 2 0446157747 USC CARE MEDICA… CAHPS for MI… NA 87 NA
#> 3 0446157747 USC CARE MEDICA… CAHPS for MI… NA NA 86
#> 4 0446157747 USC CARE MEDICA… CAHPS for MI… NA NA NA
#> 5 0446157747 USC CARE MEDICA… CAHPS for MI… NA NA NA
#> 6 0446157747 USC CARE MEDICA… CAHPS for MI… NA NA NA
#> 7 0446162697 ASSOCIATION OF … CAHPS for MI… 59 NA NA
#> 8 0446162697 ASSOCIATION OF … CAHPS for MI… NA 85 NA
#> 9 0446162697 ASSOCIATION OF … CAHPS for MI… NA NA 83
#> 10 0446162697 ASSOCIATION OF … CAHPS for MI… NA NA NA
#> # ℹ 490 more rows
#> # ℹ 3 more variables: CAHPS_GRP_5 <dbl>, CAHPS_GRP_8 <dbl>, CAHPS_GRP_12 <dbl>
不过现在看来,我们依然为每个组织设置了多行,这是因为我们并未给出哪一列具有唯一标识,可以这样给出:
cms_patient_experience |>
pivot_wider(
id_cols = starts_with("org"),
names_from = measure_cd,
values_from = prf_rate
)
#> # A tibble: 95 × 8
#> org_pac_id org_nm CAHPS_GRP_1 CAHPS_GRP_2 CAHPS_GRP_3 CAHPS_GRP_5 CAHPS_GRP_8
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 0446157747 USC C… 63 87 86 57 85
#> 2 0446162697 ASSOC… 59 85 83 63 88
#> 3 0547164295 BEAVE… 49 NA 75 44 73
#> 4 0749333730 CAPE … 67 84 85 65 82
#> 5 0840104360 ALLIA… 66 87 87 64 87
#> 6 0840109864 REX H… 73 87 84 67 91
#> 7 0840513552 SCL H… 58 83 76 58 78
#> 8 0941545784 GRITM… 46 86 81 54 NA
#> 9 1052612785 COMMU… 65 84 80 58 87
#> 10 1254237779 OUR L… 61 NA NA 65 NA
#> # ℹ 85 more rows
#> # ℹ 1 more variable: CAHPS_GRP_12 <dbl>
3.1 pivot_wider
的处理原理
我们还是举个前面类似的简单例子来探究处理原理:
df <- tribble(
~id, ~year_h, ~value,
"A", "h1", 100,
"B", "h1", 90,
"A", "h2", 130,
"B", "h2", 110,
"A", "h3", 132
)
使用pivot_wider
时将会从value列中获取值,从year_h列中获取新列名:
df |>
pivot_wider(
names_from = year_h,
values_from = value
)
#> # A tibble: 2 × 4
#> id h1 h2 h3
#> <chr> <dbl> <dbl> <dbl>
#> 1 A 100 130 132
#> 2 B 90 110 NA
这个过程是怎么样的呢?首先pivot_wider()
会弄清楚行和列中的内容,新列名就是year_h列中的唯一值:
df |>
distinct(year_h) |>
pull()
#> [1] "h1" "h2" "h3"
默认情况下,输出的行由所有不进入新名称或者值得变量确定,它们会被认作是id.cols
,这个数据集只有一列,但通常可以有多列,也可以自行指定。
df |>
select(-year_h, -value) |>
distinct()
#> # A tibble: 2 × 1
#> id
#> <chr>
#> 1 A
#> 2 B
然后pivot_wider
会将这些结果组合起来生成一个用NA填充的空数据:
df |>
select(-year_h, -value) |>
distinct() |>
mutate(h1 = NA, h2 = NA, h3 = NA)
#> # A tibble: 2 × 4
#> id h1 h2 h3
#> <chr> <lgl> <lgl> <lgl>
#> 1 A NA NA NA
#> 2 B NA NA NA
最后,pivot_wider
会使用输入的数据来填充相应的位置。
如果存在多行对应一个输出的单元格会发生什么情况呢?此时在输出的数据中将会存在列内列表的结构:
df <- tribble(
~id, ~year_h, ~value,
"A", "h1", 100,
"A", "h1", 90,
"A", "h2", 130,
"B", "h1", 110,
"B", "h2", 132
)
df |>
pivot_wider(
names_from = year_h,
values_from = value
)
#> Warning: Values from `value` are not uniquely identified; output will contain list-cols.
#> • Use `values_fn = list` to suppress this warning.
#> • Use `values_fn = {summary_fun}` to summarise duplicates.
#> • Use the following dplyr code to identify duplicates.
#> {data} %>%
#> dplyr::group_by(id, year_h) %>%
#> dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
#> dplyr::filter(n > 1L)
#> # A tibble: 2 × 3
#> id h1 h2
#> <chr> <list> <list>
#> 1 A <dbl [2]> <dbl [1]>
#> 2 B <dbl [1]> <dbl [1]>
可以通过警告信息中的提示来检查可能存在的问题:
df |>
dplyr::group_by(id, year_h) |>
dplyr::summarise(n = dplyr::n(), .groups = "drop") |>
dplyr::filter(n > 1L)
#> # A tibble: 1 × 3
#> id year_h n
#> <chr> <chr> <int>
#> 1 A h1 2
总结
至此,我们快速学完了数据分析的全过程基础,已经可以应付很多的数据处理情况了。在这一节的学习中,我们学了数据的整理,这将帮助我们获得整洁的数据。另外,我们还学到了一些扩充数据的技巧,熟练掌握这些函数的使用将在我们以后实际遇到对应情况时,快速想到相应的解决措施。我们所有学习到的知识都应该在实践中将他们运用起来。(碎碎念:这几天虽然还在家房价,但都在帮老板干活,五天开了三次会,真累啊。但是学无止境,每天还是要完成一定的学习任务的。话说老板出差为什么不能带上我去玩呢?)
这个系列还未结束,后续将会就这些步骤展开更加详细的进阶介绍。