NumPy 提供了幾個函數來從表格數據創(chuàng)建數組。我們在這里重點介紹genfromtxt
功能。
簡而言之,genfromtxt
運行兩個主循環(huán)。第一個循環(huán)將文件的每一行轉換為字符串序列。第二個循環(huán)將每個字符串轉換為適當的數據類型。這種機制比單個循環(huán)慢,但提供了更大的靈活性。特別是,?genfromtxt
能夠將丟失的數據考慮在內,而其他更快、更簡單的功能如loadtxt
不能。
的唯一強制性參數genfromtxt
是數據的來源。它可以是字符串、字符串列表、生成器或帶有read
方法的打開的類文件對象,例如文件或?io.StringIO
對象。如果提供單個字符串,則假定它是本地或遠程文件的名稱。如果提供了字符串列表或返回字符串的生成器,則每個字符串都被視為文件中的一行。當傳入遠程文件的 URL 時,該文件會自動下載到當前目錄并打開。
識別的文件類型是文本文件和檔案。目前,該功能可識別gzip
和bz2
(?bzip2
) 檔案。存檔的類型由文件的擴展名決定:如果文件名以 結尾'.gz'
,則需要gzip
存檔;如果以 結尾?'bz2'
,bzip2
則假定為存檔。
delimiter
參數一旦文件被定義并打開以供讀取,genfromtxt
?將每個非空行拆分為一系列字符串??招谢蜃⑨屝袝惶^。該delimiter
關鍵字用來定義分割應該如何發(fā)生。
通常,單個字符標志著列之間的分隔。例如,逗號分隔文件 (CSV) 使用逗號 (?,
) 或分號 (?;
) 作為分隔符:
>>> data = u"1, 2, 3\n4, 5, 6"
>>> np.genfromtxt(StringIO(data), delimiter=",")
array([[ 1., 2., 3.],
[ 4., 5., 6.]])
另一個常見的分隔符是"\t"
制表符。但是,我們不限于單個字符,任何字符串都可以。默認情況下,?genfromtxt
假設delimiter=None
,這意味著該行沿空格(包括制表符)拆分,并且連續(xù)的空格被視為單個空格。
或者,我們可能正在處理一個固定寬度的文件,其中列被定義為給定數量的字符。在這種情況下,我們需要設置?delimiter
為單個整數(如果所有列的大小相同)或整數序列(如果列可以具有不同的大?。?/p>
>>> data = u" 1 2 3\n 4 5 67\n890123 4"
>>> np.genfromtxt(StringIO(data), delimiter=3)
array([[ 1., 2., 3.],
[ 4., 5., 67.],
[ 890., 123., 4.]])
>>> data = u"123456789\n 4 7 9\n 4567 9"
>>> np.genfromtxt(StringIO(data), delimiter=(4, 3, 2))
array([[ 1234., 567., 89.],
[ 4., 7., 9.],
[ 4., 567., 9.]])
autostrip
參數默認情況下,當一行被分解為一系列字符串時,不會去除單個條目的前導空格和尾隨空格??梢酝ㄟ^將可選參數設置autostrip
為以下值來覆蓋此行為?True
:
>>> data = u"1, abc , 2\n 3, xxx, 4"
>>> # Without autostrip
>>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|U5")
array([['1', ' abc ', ' 2'],
['3', ' xxx', ' 4']], dtype='<U5')
>>> # With autostrip
>>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|U5", autostrip=True)
array([['1', 'abc', '2'],
['3', 'xxx', '4']], dtype='<U5')
comments
參數可選參數comments
用于定義標記注釋開頭的字符串。默認情況下,?genfromtxt
假設comments='#'
.?注釋標記可能出現在該行的任何位置。注釋標記之后出現的任何字符都將被忽略:
>>> data = u"""#
... # 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(StringIO(data), comments="#", delimiter=",")
array([[1., 2.],
[3., 4.],
[5., 6.],
[7., 8.],
[9., 0.]])
1.7.0 新版功能:當comments
設置為 時None
,不會將任何行視為注釋。
這種行為有一個值得注意的例外:如果可選參數?names=True
,將檢查第一個注釋行的名稱。
skip_header
和skip_footer
參數文件中標頭的存在會阻礙數據處理。在這種情況下,我們需要使用skip_header
可選參數。此參數的值必須是一個整數,它對應于在執(zhí)行任何其他操作之前要在文件開頭跳過的行數。類似地,我們可以n
通過使用skip_footer
屬性并為其賦予值來跳過文件的最后幾行n
:
>>> data = u"\n".join(str(i) for i in range(10))
>>> np.genfromtxt(StringIO(data),)
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
>>> np.genfromtxt(StringIO(data),
... skip_header=3, skip_footer=5)
array([ 3., 4.])
默認情況下,skip_header=0
和skip_footer=0
,意味著不跳過任何行。
usecols
參數在某些情況下,我們并不對數據的所有列感興趣,而只對其中的幾列感興趣。我們可以使用usecols
參數選擇要導入的列?。此參數接受與要導入的列的索引對應的單個整數或整數序列。請記住,按照慣例,第一列的索引為 0。負整數的行為與常規(guī) Python 負索引相同。
例如,如果我們只想導入第一列和最后一列,我們可以使用:usecols=(0,?-1)
>>> data = u"1 2 3\n4 5 6"
>>> np.genfromtxt(StringIO(data), usecols=(0, -1))
array([[ 1., 3.],
[ 4., 6.]])
如果列有名稱,我們還可以通過將名稱作為usecols
參數的名稱作為字符串序列或逗號分隔的字符串來選擇要導入的列:
>>> data = u"1 2 3\n4 5 6"
>>> np.genfromtxt(StringIO(data),
... names="a, b, c", usecols=("a", "c"))
array([(1.0, 3.0), (4.0, 6.0)],
dtype=[('a', '<f8'), ('c', '<f8')])
>>> np.genfromtxt(StringIO(data),
... names="a, b, c", usecols=("a, c"))
array([(1.0, 3.0), (4.0, 6.0)],
dtype=[('a', '<f8'), ('c', '<f8')])
控制我們從文件中讀取的字符串序列如何轉換為其他類型的主要方法是設置dtype
參數。此參數可接受的值為:
單一類型,例如dtype=float
.?輸出將是具有給定 dtype 的 2D,除非使用names
參數將名稱與每列關聯(見下文)。請注意,這dtype=float
是?genfromtxt
.
dtype=(int,?float,?float)
dtype="i4,f8,|U3"
.'names'
和的字典'formats'
。(name,?type)``dtype=[('A',?int),?('B',?float)]
numpy.dtype
對象。None
。在這種情況下,列的類型將由數據本身確定(見下文)。
在除第一種情況之外的所有情況下,輸出將是具有結構化 dtype 的一維數組。此 dtype 具有與序列中的項目一樣多的字段。字段名稱是用names
關鍵字定義的。
當 時dtype=None
,每列的類型由其數據迭代確定。我們首先檢查字符串是否可以轉換為布爾值(即字符串是否匹配true
或false
小寫);然后它是否可以轉換為整數,然后轉換為浮點數,然后轉換為復數,最終轉換為字符串??梢酝ㄟ^修改類的默認映射器來更改此行為?StringConverter
。
dtype=None
提供該選項是為了方便。但是,它比顯式設置 dtype 慢得多。
names
參數處理表格數據時的一種自然方法是為每一列分配一個名稱。第一種可能性是使用顯式結構化 dtype,如前所述:
>>> data = StringIO("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 = StringIO("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。
我們有時可能需要從數據本身定義列名。在這種情況下,我們必須使用names
值為的關鍵字?True
。然后將從第一行(在skip_header
那些之后)讀取名稱?,即使該行被注釋掉:
>>> data = StringIO("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 = StringIO("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
參數如果names=None
但需要結構化 dtype,則使用標準 NumPy 默認值定義"f%i"
名稱,產生類似 的名稱f0
,?f1
依此類推:
>>> data = StringIO("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 = StringIO("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
參數覆蓋這個默認值,它接受任何格式字符串:
>>> data = StringIO("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
只有在預期某些名稱但未定義時才使用它。
也可以將具有結構化數據類型的 NumPy 數組視為?recarray
,其中可以像訪問屬性一樣訪問字段。出于這個原因,我們可能需要確保字段名稱不包含任何空格或無效字符,或者它不對應于標準屬性的名稱(如size
或?shape
),這會混淆解釋器。?genfromtxt
?接受三個可選參數,可以更好地控制名稱:
deletechars
給出一個字符串,該字符串組合了必須從名稱中刪除的所有字符。默認情況下,無效字符為?.~!@#$%^&*()-=+~\|]}[{';:?/?.>,<
excludelist
給出要排除的名稱列表,例如return
,?file
,?print
... 如果輸入名稱之一是此列表的一部分,'_'
則將在其后附加下劃線字符 (?)。case_sensitive
名稱是否應區(qū)分大小寫 (?case_sensitive=True
)、轉換為大寫 (case_sensitive=False
或?case_sensitive='upper'
) 或小寫 (?case_sensitive='lower'
)。converters
參數通常,定義 dtype 足以定義必須如何轉換字符串序列。然而,有時可能需要一些額外的控制。例如,我們可能希望確保將某種格式的日期?YYYY/MM/DD
轉換為datetime
對象,或者將類似字符串xx%
正確轉換為介于 0 和 1 之間的浮點數。在這種情況下,我們應該使用converters
?參數定義轉換函數。
此參數的值通常是一個字典,其中列索引或列名作為鍵,轉換函數作為值。這些轉換函數可以是實際函數或 lambda 函數。在任何情況下,它們都應該只接受一個字符串作為輸入,并且只輸出所需類型的單個元素。
在以下示例中,第二列從表示百分比的字符串轉換為介于 0 和 1 之間的浮點數:
>>> convertfunc = lambda x: float(x.strip(b"%"))/100.
>>> data = u"1, 2.3%, 45.\n6, 78.9%, 0"
>>> names = ("i", "p", "n")
>>> # General case .....
>>> np.genfromtxt(StringIO(data), delimiter=",", names=names)
array([(1., nan, 45.), (6., nan, 0.)],
dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])
我們需要記住,默認情況下,dtype=float
.?因此,預計第二列會出現浮點數。但是,字符串?和不能轉換為浮點數,我們最終得到了??,F在讓我們使用轉換器:'?2.3%'``'?78.9%'``np.nan
>>> # Converted case ...
>>> np.genfromtxt(StringIO(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(StringIO(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 = u"1, , 3\n 4, 5, 6"
>>> convert = lambda x: float(x.strip() or -999)
>>> np.genfromtxt(StringIO(data), delimiter=",",
... converters={1: convert})
array([[ 1., -999., 3.],
[ 4., 5., 6.]])
我們嘗試導入的數據集中可能缺少某些條目。在前面的示例中,我們使用轉換器將空字符串轉換為浮點數。然而,用戶定義的轉換器可能很快變得難以管理。
該genfromtxt
函數提供了另外兩種補充機制:missing_values
參數用于識別缺失數據,第二個參數filling_values
用于處理這些缺失數據。
missing_values
默認情況下,任何空字符串都被標記為缺失。我們還可以考慮更復雜的字符串,例如"N/A"
或"???"
來表示丟失或無效的數據。該missing_values
參數接受三個類型的值:
None
可用于定義適用于所有列的默認值。filling_values
我們知道如何識別缺失的數據,但我們仍然需要為這些缺失的條目提供一個值。默認情況下,此值是根據下表根據預期的 dtype 確定的:
預期類型 | 默認 |
---|---|
bool |
False |
int |
-1 |
float |
np.nan |
complex |
np.nan+0j |
string |
'???' |
我們可以使用filling_values
可選參數更好地控制缺失值的轉換?。就像?missing_values
,這個參數接受不同類型的值:
None
為所有列定義默認值。
在下面的示例中,我們假設缺失值"N/A"
在第一列中標記為 with?,"???"
在第三列中標記為by?。如果它們出現在第一列和第二列,我們希望將這些缺失值轉換為 0,如果它們出現在最后一列,則轉換為 -999:
>>> data = u"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(StringIO(data), **kwargs)
array([(0, 2, 3), (4, 0, -999)],
dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])
usemask
我們可能還想通過構建一個布爾掩碼來跟蹤丟失數據的發(fā)生情況,True
其中包含數據丟失的條目等False
。為此,我們只需將可選參數設置usemask
為True
(默認為False
)。輸出數組將是一個MaskedArray
.
此外genfromtxt
,該numpy.lib.npyio
模塊提供了幾個從?genfromtxt
.?這些函數的工作方式與原始函數相同,但它們具有不同的默認值。
recfromtxt
返回標準numpy.recarray
(if?usemask=False
) 或?MaskedRecords
數組 (if?usemaske=True
)。默認的 dtype 是dtype=None
,這意味著將自動確定每列的類型。recfromcsv
喜歡recfromtxt
,但有一個默認值delimiter=","
。
更多建議: