NumPy與輸入輸出
使用genfromtxt導入數(shù)據(jù)
NumPy提供了幾個函數(shù)來根據(jù)表格數(shù)據(jù)創(chuàng)建數(shù)組。我們將重點放在genfromtxt
函數(shù)上。
In a nutshell, genfromtxt
runs two main loops. 第一個循環(huán)以字符串序列轉換文件的每一行。第二個循環(huán)將每個字符串轉換為適當?shù)臄?shù)據(jù)類型。這種機制比單一循環(huán)慢,但提供了更多的靈活性。特別的, genfromtxt
考慮到缺失值的情況, 其他更簡單的方法如loadtxt
無法做到這點.
注意
舉例時,我們將使用以下約定:
>>> import numpy as np
>>> from io import BytesIO
定義輸入
genfromtxt
的唯一強制參數(shù)是數(shù)據(jù)的來源。它可以是一個字符串,一串字符串或一個生成器。如果提供了單個字符串,則假定它是本地或遠程文件的名稱,或者帶有read
方法的開放文件類對象,例如文件或StringIO.StringIO
對象。如果提供了字符串列表或生成器返回字符串,則每個字符串在文件中被視為一行。當傳遞遠程文件的URL時,該文件將自動下載到當前目錄并打開。
識別的文件類型是文本文件和檔案。目前,該功能可識別gzip
和bz2
(bzip2)檔案。歸檔文件的類型由文件的擴展名決定:如果文件名以'.gz'
結尾,則需要一個gzip
歸檔文件;如果它以'bz2'
結尾,則假定bzip2
存檔。
將行拆分為列
delimiter
參數(shù)
一旦文件被定義并打開進行讀取,genfromtxt
會將每個非空行分割為一串字符串。 空的或注釋的行只是略過。 delimiter
關鍵字用于定義拆分應該如何進行。
通常,單個字符標記列之間的分隔。例如,逗號分隔文件(CSV)使用逗號(,
)或分號(;
)作為分隔符:
>>> data = "1, 2, 3\n4, 5, 6"
>>> np.genfromtxt(BytesIO(data), delimiter=",")
array([[ 1., 2., 3.],
[ 4., 5., 6.]])
另一個常用的分隔符是"\t",即制表符。但是,我們不限于單個字符,任何字符串都可以。默認情況下,genfromtxt
假定delimiter=None
,這意味著該行沿著空白區(qū)域(包括制表符)分割,并且連續(xù)的空白區(qū)域被視為單個空白區(qū)域。
或者,我們可能正在處理一個固定寬度的文件,其中列被定義為給定數(shù)量的字符。在這種情況下,我們需要將delimiter
設置為單個整數(shù)(如果所有列的大小相同)或整數(shù)序列(如果列的大小可能不同):
>>> data = " 1 2 3\n 4 5 67\n890123 4"
>>> np.genfromtxt(BytesIO(data), delimiter=3)
array([[ 1., 2., 3.],
[ 4., 5., 67.],
[ 890., 123., 4.]])
>>> data = "123456789\n 4 7 9\n 4567 9"
>>> np.genfromtxt(BytesIO(data), delimiter=(4, 3, 2))
array([[ 1234., 567., 89.],
[ 4., 7., 9.],
[ 4., 567., 9.]])
autostrip
參數(shù)
默認情況下,當一行被分解為一系列字符串時,單個條目不會被剝離前導空白或尾隨空白。通過將可選參數(shù)autostrip設置為值True,可以覆蓋此行為:
>>> data = "1, abc , 2\n 3, xxx, 4"
>>> # Without autostrip
>>> np.genfromtxt(BytesIO(data), delimiter=",", dtype="|S5")
array([['1', ' abc ', ' 2'],
['3', ' xxx', ' 4']],
dtype='|S5')
>>> # With autostrip
>>> np.genfromtxt(BytesIO(data), delimiter=",", dtype="|S5", autostrip=True)
array([['1', 'abc', '2'],
['3', 'xxx', '4']],
dtype='|S5')
comments
參數(shù)
可選參數(shù)comments
用于定義標記注釋開始的字符串。默認情況下,genfromtxt
假定comments='#'
。評論標記可能發(fā)生在線上的任何地方。評論標記之后的任何字符都會被忽略:
>>> data = """#
... # Skip me !
... # Skip me too !
... 1, 2
... 3, 4
... 5, 6 #This is the third line of the data
... 7, 8
... # And here comes the last line
... 9, 0
... """
>>> np.genfromtxt(BytesIO(data), comments="#", delimiter=",")
[[ 1. 2.]
[ 3. 4.]
[ 5. 6.]
[ 7. 8.]
[ 9. 0.]]
注意
這種行為有一個明顯的例外:如果可選參數(shù)names=True
,則會檢查第一條注釋行的名稱。
跳過直線并選擇列
skip_header
和skip_footer
參數(shù)
文件中存在標題可能會妨礙數(shù)據(jù)處理。在這種情況下,我們需要使用skip_header
可選參數(shù)。此參數(shù)的值必須是一個整數(shù),與執(zhí)行任何其他操作之前在文件開頭跳過的行數(shù)相對應。同樣,我們可以使用skip_footer
屬性跳過文件的最后一行n
,并給它一個n
的值:
>>> data = "\n".join(str(i) for i in range(10))
>>> np.genfromtxt(BytesIO(data),)
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
>>> np.genfromtxt(BytesIO(data),
... skip_header=3, skip_footer=5)
array([ 3., 4.])
默認情況下,skip_header=0
和skip_footer=0
,這意味著不會跳過任何行。
usecols
參數(shù)
在某些情況下,我們對數(shù)據(jù)的所有列不感興趣,但只有其中的一小部分。我們可以用usecols
參數(shù)選擇要導入的列。該參數(shù)接受與要導入的列的索引相對應的單個整數(shù)或整數(shù)序列。請記住,按照慣例,第一列的索引為0。負整數(shù)的行為與常規(guī)Python負向索引相同。
例如,如果我們只想導入第一列和最后一列,我們可以使用usecols =(0, -1)
:
>>> data = "1 2 3\n4 5 6"
>>> np.genfromtxt(BytesIO(data), usecols=(0, -1))
array([[ 1., 3.],
[ 4., 6.]])
如果列有名稱,我們也可以通過將它們的名稱提供給usecols
參數(shù)來選擇要導入哪些列,可以將其作為字符串序列或逗號分隔字符串:
>>> data = "1 2 3\n4 5 6"
>>> np.genfromtxt(BytesIO(data),
... names="a, b, c", usecols=("a", "c"))
array([(1.0, 3.0), (4.0, 6.0)],
dtype=[('a', '<f8'), ('c', '<f8')])
>>> np.genfromtxt(BytesIO(data),
... names="a, b, c", usecols=("a, c"))
array([(1.0, 3.0), (4.0, 6.0)],
dtype=[('a', '<f8'), ('c', '<f8')])
選擇數(shù)據(jù)的類型
控制我們從文件中讀取的字符串序列如何轉換為其他類型的主要方法是設置dtype
參數(shù)。這個參數(shù)的可接受值是:
- 單一類型,如
dtype=float
。除非使用names
參數(shù)將名稱與每個列關聯(lián)(見下文),否則輸出將是給定dtype的2D格式。請注意,dtype=float
是genfromtxt
的默認值。 - 一系列類型,如
dtype =(int, float, float)
。 - 逗號分隔的字符串,例如
dtype="i4,f8,|S3"
。 - 一個包含兩個鍵
'names'
和'formats'
的字典。 - a sequence of tuples
(name, type)
, such asdtype=[('A', int), ('B', float)]
. - 現(xiàn)有的
numpy.dtype
對象。 - 特殊值
None
。在這種情況下,列的類型將根據(jù)數(shù)據(jù)本身確定(見下文)。
在所有情況下,除了第一種情況,輸出將是一個帶有結構化dtype的一維數(shù)組。這個dtype與序列中的項目一樣多。字段名稱由names
關鍵字定義。
當dtype=None
時,每列的類型由其數(shù)據(jù)迭代確定。我們首先檢查一個字符串是否可以轉換為布爾值(也就是說,如果字符串在小寫字母中匹配true
或false
);然后是否可以將其轉換為整數(shù),然后轉換為浮點數(shù),然后轉換為復數(shù)并最終轉換為字符串。通過修改StringConverter
類的默認映射器可以更改此行為。
為方便起見,提供了dtype=None選項。但是,它明顯比顯式設置dtype要慢。
設置名稱
names
參數(shù)
處理表格數(shù)據(jù)時的一種自然方法是為每列分配一個名稱。如前所述,第一種可能性是使用明確的結構化dtype。
>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=[(_, int) for _ in "abc"])
array([(1, 2, 3), (4, 5, 6)],
dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])
另一種更簡單的可能性是將names
關鍵字與一系列字符串或逗號分隔的字符串一起使用:
>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, names="A, B, C")
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)],
dtype=[('A', '<f8'), ('B', '<f8'), ('C', '<f8')])
在上面的例子中,我們使用了默認情況下dtype=float
的事實。通過給出一個名稱序列,我們強制輸出到一個結構化的dtype。
我們有時可能需要從數(shù)據(jù)本身定義列名。在這種情況下,我們必須使用names
關鍵字的值為True
。這些名字將從第一行(在skip_header
之后)被讀取,即使該行被注釋掉:
>>> data = BytesIO("So it goes\n#a b c\n1 2 3\n 4 5 6")
>>> np.genfromtxt(data, skip_header=1, names=True)
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)],
dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
names
的默認值為None
。如果我們給關鍵字賦予任何其他值,新名稱將覆蓋我們可能用dtype定義的字段名稱:
>>> data = BytesIO("1 2 3\n 4 5 6")
>>> ndtype=[('a',int), ('b', float), ('c', int)]
>>> names = ["A", "B", "C"]
>>> np.genfromtxt(data, names=names, dtype=ndtype)
array([(1, 2.0, 3), (4, 5.0, 6)],
dtype=[('A', '<i8'), ('B', '<f8'), ('C', '<i8')])
defaultfmt
參數(shù)
如果 names=None
的時候,只是預計會有一個結構化的dtype,它的名稱將使用標準的NumPy默認值 "f%i"
來定義,會產生例如f0
,f1
等名稱:
>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int))
array([(1, 2.0, 3), (4, 5.0, 6)],
dtype=[('f0', '<i8'), ('f1', '<f8'), ('f2', '<i8')])
同樣,如果我們沒有提供足夠的名稱來匹配dtype的長度,缺少的名稱將使用此默認模板進行定義:
>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int), names="a")
array([(1, 2.0, 3), (4, 5.0, 6)],
dtype=[('a', '<i8'), ('f0', '<f8'), ('f1', '<i8')])
我們可以使用defaultfmt
參數(shù)覆蓋此默認值,該參數(shù)采用任何格式字符串:
>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int), defaultfmt="var_%02i")
array([(1, 2.0, 3), (4, 5.0, 6)],
dtype=[('var_00', '<i8'), ('var_01', '<f8'), ('var_02', '<i8')])
注意!
我們需要記住,僅當預期一些名稱但未定義時才使用defaultfmt
。
驗證名稱
具有結構化dtype的NumPy數(shù)組也可以被視為recarray
,其中可以像訪問屬性一樣訪問字段。因此,我們可能需要確保字段名稱不包含任何空格或無效字符,或者它不對應于標準屬性的名稱(如size
或shape
),這會混淆解釋者。genfromtxt
接受三個可選參數(shù),這些參數(shù)可以更好地控制名稱:
deletechars
- 給出一個字符串,將所有必須從名稱中刪除的字符組合在一起。默認情況下,無效字符是~!@#$%^&*()-=+~\|]}[{';: /?.>,<
excludelist
- 給出要排除的名稱列表,如return
,file
,print
...如果其中一個輸入名稱是該列表的一部分,則會附加一個下劃線字符('_'
)。case_sensitive
- 是否區(qū)分大小寫(case_sensitive=True
),轉換為大寫(case_sensitive=False
或case_sensitive='upper'
)或小寫(case_sensitive='lower'
)。
調整轉換
converters
參數(shù)
通常,定義一個dtype足以定義字符串序列必須如何轉換。但是,有時可能需要一些額外的控制。例如,我們可能希望確保格式為YYYY/MM/DD
的日期轉換為datetime
對象,或者像xx%
正確轉換為0到1之間的浮點數(shù)。在這種情況下,我們應該使用converters
參數(shù)定義轉換函數(shù)。
該參數(shù)的值通常是以列索引或列名稱作為關鍵字的字典,并且轉換函數(shù)作為值。這些轉換函數(shù)可以是實際函數(shù)或lambda函數(shù)。無論如何,它們只應接受一個字符串作為輸入,并只輸出所需類型的單個元素。
在以下示例中,第二列從代表百分比的字符串轉換為0和1之間的浮點數(shù):
>>> convertfunc = lambda x: float(x.strip("%"))/100.
>>> data = "1, 2.3%, 45.\n6, 78.9%, 0"
>>> names = ("i", "p", "n")
>>> # General case .....
>>> np.genfromtxt(BytesIO(data), delimiter=",", names=names)
array([(1.0, nan, 45.0), (6.0, nan, 0.0)],
dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])
我們需要記住,默認情況下,dtype=float
。因此,對于第二列期望浮點數(shù)。但是,字符串'2.3%'
和'78.9%
無法轉換為浮點數(shù),我們最終改為使用np.nan
?,F(xiàn)在讓我們使用一個轉換器:
>>> # Converted case ...
>>> np.genfromtxt(BytesIO(data), delimiter=",", names=names,
... converters={1: convertfunc})
array([(1.0, 0.023, 45.0), (6.0, 0.78900000000000003, 0.0)],
dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])
通過使用第二列("p"
)作為關鍵字而不是其索引(1)的名稱,可以獲得相同的結果:
>>> # Using a name for the converter ...
>>> np.genfromtxt(BytesIO(data), delimiter=",", names=names,
... converters={"p": convertfunc})
array([(1.0, 0.023, 45.0), (6.0, 0.78900000000000003, 0.0)],
dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])
轉換器也可以用來為缺少的條目提供默認值。在以下示例中,如果字符串為空,則轉換器convert
會將已剝離的字符串轉換為相應的浮點型或轉換為-999。我們需要明確地從空白處去除字符串,因為它并未默認完成:
>>> data = "1, , 3\n 4, 5, 6"
>>> convert = lambda x: float(x.strip() or -999)
>>> np.genfromtxt(BytesIO(data), delimiter=",",
... converters={1: convert})
array([[ 1., -999., 3.],
[ 4., 5., 6.]])
使用缺失值和填充值
我們嘗試導入的數(shù)據(jù)集中可能缺少一些條目。在前面的例子中,我們使用轉換器將空字符串轉換為浮點。但是,用戶定義的轉換器可能會很快變得繁瑣,難以管理。
genfromtxt
函數(shù)提供了另外兩種補充機制:missing_values
參數(shù)用于識別丟失的數(shù)據(jù),第二個參數(shù)filling_values
用于處理這些缺失的數(shù)據(jù)。
missing_values
默認情況下,任何空字符串都被標記為缺失。我們也可以考慮更復雜的字符串,比如"N/A"
或"???"
代表丟失或無效的數(shù)據(jù)。missing_values
參數(shù)接受三種值:
- 單個字符串或逗號分隔的字符串 - 該字符串將用作所有列缺失數(shù)據(jù)的標記
- 字符串 - 在這種情況下,每個項目都按順序與列關聯(lián)。
- 字典類型 - 字典的值是字符串或字符串序列。相應的鍵可以是列索引(整數(shù))或列名稱(字符串)。另外,可以使用特殊鍵None來定義適用于所有列的默認值。
filling_values
我們知道如何識別丟失的數(shù)據(jù),但我們仍然需要為這些丟失的條目提供一個值。默認情況下,根據(jù)此表根據(jù)預期的dtype確定此值:
我們知道如何識別丟失的數(shù)據(jù),但我們仍然需要為這些丟失的條目提供一個值。默認情況下,根據(jù)此表根據(jù)預期的dtype確定此值:
預期類型 | 默認 |
---|---|
bool |
False |
int |
-1 |
float |
np.nan |
complex |
np.nan+0j |
string |
'???' |
通過filling_values
可選參數(shù),我們可以更好地控制缺失值的轉換。像missing_values
一樣,此參數(shù)接受不同類型的值:
- 單個值 - 這將是所有列的默認值
- 類數(shù)組類型 - 每個條目都是相應列的默認值
- 字典類型 - 每個鍵可以是列索引或列名稱,并且相應的值應該是單個對象。我們可以使用特殊鍵None為所有列定義默認值。
在下面的例子中,我們假設缺少的值在第一列中用"N/A"
標記,并由"???"
在第三欄。如果它們出現(xiàn)在第一列和第二列中,我們希望將這些缺失值轉換為0,如果它們出現(xiàn)在最后一列中,則將它們轉換為-999:
>>> data = "N/A, 2, 3\n4, ,???"
>>> kwargs = dict(delimiter=",",
... dtype=int,
... names="a,b,c",
... missing_values={0:"N/A", 'b':" ", 2:"???"},
... filling_values={0:0, 'b':0, 2:-999})
>>> np.genfromtxt(BytesIO(data), **kwargs)
array([(0, 2, 3), (4, 0, -999)],
dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])
usemask
我們也可能想通過構造一個布爾掩碼來跟蹤丟失數(shù)據(jù)的發(fā)生,其中True
條目缺少數(shù)據(jù),否則False
。為此,我們只需將可選參數(shù)usemask
設置為True
(默認值為False
)。輸出數(shù)組將成為MaskedArray
。
快捷方式函數(shù)
除了 genfromtxt
之外,numpy.lib.io模塊還提供了幾個從genfromtxt
派生的方便函數(shù)。這些函數(shù)的工作方式與原始函數(shù)相同,但它們具有不同的默認值。
- recfromtxt - 返回標準 numpy.recarray
- (如果
usemask=False
)或 MaskedRecords數(shù)組(如果usemaske=True
)。默認dtype是dtype=None
,意味著將自動確定每列的類型。 - recfromcsv - 類似 recfromtxt,但有默認值
delimiter=","
。
作者:柯廣的網絡日志 ? NumPy與輸入輸出
微信公眾號:Java大數(shù)據(jù)與數(shù)據(jù)倉庫