总览
任何应用程序最重要的方面之一就是验证其输入。 如果输入不满足要求,最基本的方法就是失败。 但是,在许多情况下,这还不够。 在许多系统中,数据收集与数据分析是分开的。 它可能是调查或旧数据集。
在这些情况下,有必要在分析之前遍历整个数据集,检测无效或丢失的数据,修复可修复的内容,并标记或删除无法挽救的数据。 提供有关数据质量和遇到哪种错误的统计信息也很有用。
在这个由两部分组成的系列文章中,您将学习如何使用Go的文本工具,对CSV文件进行切片和切块,并确保数据一尘不染。 在第一部分中,我们将重点介绍Go语言中文本,字节,符文和字符串等文本处理的基础,以及使用CSV文件的基础。
Go中的文字
在深入研究数据清理之前,让我们从Go的文本基础开始。 构建块是字节,符文和字符串。 让我们看看每个人代表什么以及他们之间的关系。
字节数
字节是8位数字。 每个字节可以表示可能的256个值之一(2的8的幂)。 ASCII字符集中的每个字符都可以由一个字节表示。 但是字节不是字符。 原因是Go作为一种现代语言支持Unicode,其中有超过256个单独的字符。 输入符文。
符文
Go中的符文是int32类型的别称。 这意味着每个符文可以表示超过40亿个单独的值(2等于32的幂),足以覆盖整个Unicode字符集。
在以下代码中,您可以看到符文“ ∆”(在Mac上为alt-J)只是一个int32。 为了将它代表的字符打印到屏幕上,我必须将其转换为字符串。
package main
import (
"fmt"
)
func main() {
r := '∆'
fmt.Println(r)
fmt.Println(int32(r))
fmt.Println(string(r))
}
Output:
8710
8710
∆
Unicode很复杂。 符文正式代表Unicode代码点。 Unicode字符通常由单个Unicode代码点表示,但有时不止一个。
弦乐
字符串正式是只读的字节片。 如果索引一个字符串,则返回一个字节:
func main() {
s := "abc"
for i := 0; i < len(s); i++ {
fmt.Println(s[i])
}
}
Output:
97
98
99
字符串文字是用双引号括起来的一系列UTF-8字符。 它们可能包含转义序列,它们是反斜杠,后跟ASCII字符,例如\n
(换行符)或\t
(制表符)。 它们具有特殊的含义。 这是完整列表:
\a U+0007 alert or bell
\b U+0008 backspace
\f U+000C form feed
\n U+000A line feed or newline
\r U+000D carriage return
\t U+0009 horizontal tab
\v U+000b vertical tab
\\ U+005c backslash
\' U+0027 single quote (valid only within rune literals)
\" U+0022 double quote (valid only within string literals)
有时,您可能希望直接将文字字节存储在字符串中,而与转义序列无关。 您可以避开每个反斜杠,但这很乏味。 更好的方法是使用反引号中包含的原始字符串。
这是一个带有\t
(制表符)转义序列的字符串的示例,该字符串按原样表示一次,然后使用反斜杠转义,然后作为原始字符串表示:
func main() {
s1 := "1\t2"
s2 := "1\\t2"
s3 := `1\t2`
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
}
Output:
1 2
1\t2
1\t2
尽管字符串是字节的切片,但是当您使用for-range语句遍历字符串时,每次迭代都会得到一个符文。 这意味着您可能会获得一个或多个字节。 使用for-range索引很容易看到这一点。 这是一个疯狂的例子。 希伯来语单词“שלום”的意思是“你好”(和和平)。 希伯来语也从右到左书写。 我将构造一个将希伯来语单词及其英语翻译混合在一起的字符串。
然后,我将按符文打印该符文,包括字符串中每个符文的字节索引。 如您所见,每个希伯来文符文占用两个字节,而英文字符则占用一个字节,因此即使该字符串有四个希伯来语字符,三个符号和五个英文字符(12个字符),该字符串的总长度为16个字节。 )。 此外,希伯来语字符将从右向左显示:
func main() {
hello := "שלום = hello"
fmt.Println("length:", len(hello))
for i, r := range(hello) {
fmt.Println(i, string(r))
}
}
Output:
length: 16
0 ש
2 ל
4 ו
6 ם
8
9 =
10
11 h
12 e
13 l
14 l
15 o
当您有一个数据集要用怪异的引号和Unicode字符和符号混合进行清理时,所有这些细微差别都非常重要。
当打印字符串和字节片时,有几种格式说明符在两者上都相同。 %s
格式按原样打印字节, %x
每个字节打印两个小写十六进制字符, %X
每个字节打印两个大写十六进制字符, %q
打印使用go语法转义的双引号字符串。
要在格式字符串说明符中转义%符号,只需将其加倍即可。 要在使用%x
或%X
时分隔字节,可以添加空格,如“%x”和“%X”。 这是演示:
func main() {
s := "שלום"
fmt.Printf("%%s format: %s\n", s)
fmt.Printf("%%x format: %x\n", s)
fmt.Printf("%%X format: %X\n", s)
fmt.Printf("%% x format: % x\n", s)
fmt.Printf("%% X format: % X\n", s)
fmt.Printf("%%q format: %q\n", s)
}
Output:
%s format: שלום
%x format: d7a9d79cd795d79d
%X format: D7A9D79CD795D79D
% x format: d7 a9 d7 9c d7 95 d7 9d
% X format: D7 A9 D7 9C D7 95 D7 9D
%q format: "שלום"
读写CSV文件
数据可以多种方式到达。 CSV(逗号分隔值)是最常见的格式之一。 CSV数据非常有效。 文件通常在标题行中包含字段或数据列和数据行的名称,其中每一行包含每个字段的值,并用逗号分隔。
这是UFO目击数据集的一个小片段(确实)。 第一行(标题)包含列名,其他行包含数据。 您会发现“报告的颜色”列通常为空:
City,Colors Reported,Shape Reported,State,Time
Ithaca,,TRIANGLE,NY,6/1/1930 22:00
Willingboro,,OTHER,NJ,6/30/1930 20:00
Holyoke,,OVAL,CO,2/15/1931 14:00
Abilene,,DISK,KS,6/1/1931 13:00
New York Worlds Fair,,LIGHT,NY,4/18/1933 19:00
Valley City,,DISK,ND,9/15/1934 15:30
Crater Lake,,CIRCLE,CA,6/15/1935 0:00
Alma,,DISK,MI,7/15/1936 0:00
Eklutna,,CIGAR,AK,10/15/1936 17:00
Hubbard,,CYLINDER,OR,6/15/1937 0:00
Fontana,,LIGHT,CA,8/15/1937 21:00
Waterloo,,FIREBALL,AL,6/1/1939 20:00
Belton,RED,SPHERE,SC,6/30/1939 20:00
将这部分CSV数据写入文件需要进行一些字符串操作以及使用文件。 在深入探讨主要逻辑之前,这里有一些必不可少的部分:包定义,导入和数据字符串(请注意const
的使用)。
package main
import (
"os"
"strings"
"bufio"
)
data := `
City,Colors Reported,Shape Reported,State,Time
Ithaca,,TRIANGLE,NY,6/1/1930 22:00
Willingboro,,OTHER,NJ,6/30/1930 20:00
Holyoke,,OVAL,CO,2/15/1931 14:00
Abilene,,DISK,KS,6/1/1931 13:00
New York Worlds Fair,,LIGHT,NY,4/18/1933 19:00
Valley City,,DISK,ND,9/15/1934 15:30
Crater Lake,,CIRCLE,CA,6/15/1935 0:00
Alma,,DISK,MI,7/15/1936 0:00
Eklutna,,CIGAR,AK,10/15/1936 17:00
Hubbard,,CYLINDER,OR,6/15/1937 0:00
Fontana,,LIGHT,CA,8/15/1937 21:00
Waterloo,,FIREBALL,AL,6/1/1939 20:00
Belton,RED,SPHERE,SC,6/30/1939 20:00
`
main()
函数创建一个名为“ ufo-sightings.csv”的文件,检查是否没有错误,然后创建一个缓冲的编写器w
。 下一行的defer
调用将缓冲区的内容刷新到文件中,该调用在函数末尾执行。 那就是延期的意思。 然后,它使用字符串包的Split()
函数将数据字符串分成几行。
然后,在for循环内部,从每行修剪前导和尾随空白。 空行将被跳过,非空行将被写入缓冲区,后跟换行符。 而已。 最后,缓冲区将刷新到文件中。
func main() {
f, err := os.Create("ufo-sightings.csv")
if err != nil {
panic(e)
}
w := bufio.NewWriter(f)
defer w.Flush()
lines := strings.Split(data, "\n")
for _, line := range lines {
line := strings.Trim(line, " ")
if line == "" {
continue
}
w.WriteString(line)
w.WriteString("\n")
}
}
从文件读取非常简单:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
data, err := ioutil.ReadFile("ufo-sightings.csv")
if err != nil {
panic(err)
}
fmt.Println(string(data))
}
结论
Go具有强大的功能来处理各种形状和编码的文本。 在本系列的这一部分中,我们研究了Go语言中的文本表示,使用字符串包进行文本处理以及处理CSV文件的基础知识。
在第二部分中,我们将把学到的知识付诸实践,以清理凌乱的数据,为分析做准备。
翻译自: https://code.tutsplus.com/tutorials/cleaning-up-your-data-with-go-part-1--cms-30298