NumPy 導(dǎo)入數(shù)據(jù) genfromtxt

2021-09-01 10:06 更新

NumPy 提供了幾個函數(shù)來從表格數(shù)據(jù)創(chuàng)建數(shù)組。我們在這里重點介紹genfromtxt功能。

簡而言之,genfromtxt運行兩個主循環(huán)。第一個循環(huán)將文件的每一行轉(zhuǎn)換為字符串序列。第二個循環(huán)將每個字符串轉(zhuǎn)換為適當(dāng)?shù)臄?shù)據(jù)類型。這種機制比單個循環(huán)慢,但提供了更大的靈活性。特別是,?genfromtxt能夠?qū)G失的數(shù)據(jù)考慮在內(nèi),而其他更快、更簡單的功能如loadtxt不能。

1、定義輸入

的唯一強制性參數(shù)genfromtxt是數(shù)據(jù)的來源。它可以是字符串、字符串列表、生成器或帶有read方法的打開的類文件對象,例如文件或?io.StringIO對象。如果提供單個字符串,則假定它是本地或遠(yuǎn)程文件的名稱。如果提供了字符串列表或返回字符串的生成器,則每個字符串都被視為文件中的一行。當(dāng)傳入遠(yuǎn)程文件的 URL 時,該文件會自動下載到當(dāng)前目錄并打開。

識別的文件類型是文本文件和檔案。目前,該功能可識別gzipbz2(?bzip2) 檔案。存檔的類型由文件的擴展名決定:如果文件名以 結(jié)尾'.gz',則需要gzip存檔;如果以 結(jié)尾?'bz2',bzip2則假定為存檔。

2、將行拆分為列

2.1 該delimiter參數(shù)

一旦文件被定義并打開以供讀取,genfromtxt?將每個非空行拆分為一系列字符串??招谢蜃⑨屝袝惶^。該delimiter關(guān)鍵字用來定義分割應(yīng)該如何發(fā)生。

通常,單個字符標(biāo)志著列之間的分隔。例如,逗號分隔文件 (CSV) 使用逗號 (?,) 或分號 (?;) 作為分隔符:

>>> data = u"1, 2, 3\n4, 5, 6"
>>> np.genfromtxt(StringIO(data), delimiter=",")
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])

另一個常見的分隔符是"\t"制表符。但是,我們不限于單個字符,任何字符串都可以。默認(rèn)情況下,?genfromtxt假設(shè)delimiter=None,這意味著該行沿空格(包括制表符)拆分,并且連續(xù)的空格被視為單個空格。

或者,我們可能正在處理一個固定寬度的文件,其中列被定義為給定數(shù)量的字符。在這種情況下,我們需要設(shè)置?delimiter為單個整數(shù)(如果所有列的大小相同)或整數(shù)序列(如果列可以具有不同的大?。?/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.]])

2.2 autostrip參數(shù)

默認(rèn)情況下,當(dāng)一行被分解為一系列字符串時,不會去除單個條目的前導(dǎo)空格和尾隨空格??梢酝ㄟ^將可選參數(shù)設(shè)置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')

2.3 comments參數(shù)

可選參數(shù)comments用于定義標(biāo)記注釋開頭的字符串。默認(rèn)情況下,?genfromtxt假設(shè)comments='#'.?注釋標(biāo)記可能出現(xiàn)在該行的任何位置。注釋標(biāo)記之后出現(xiàn)的任何字符都將被忽略:

>>> 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 新版功能:當(dāng)comments設(shè)置為 時None,不會將任何行視為注釋。

筆記

這種行為有一個值得注意的例外:如果可選參數(shù)?names=True,將檢查第一個注釋行的名稱。

3、跳過行和選擇列

3.1 在skip_headerskip_footer參數(shù)

文件中標(biāo)頭的存在會阻礙數(shù)據(jù)處理。在這種情況下,我們需要使用skip_header可選參數(shù)。此參數(shù)的值必須是一個整數(shù),它對應(yīng)于在執(zhí)行任何其他操作之前要在文件開頭跳過的行數(shù)。類似地,我們可以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.])

默認(rèn)情況下,skip_header=0skip_footer=0,意味著不跳過任何行。

3.2 usecols參數(shù)

在某些情況下,我們并不對數(shù)據(jù)的所有列感興趣,而只對其中的幾列感興趣。我們可以使用usecols參數(shù)選擇要導(dǎo)入的列?。此參數(shù)接受與要導(dǎo)入的列的索引對應(yīng)的單個整數(shù)或整數(shù)序列。請記住,按照慣例,第一列的索引為 0。負(fù)整數(shù)的行為與常規(guī) Python 負(fù)索引相同。

例如,如果我們只想導(dǎo)入第一列和最后一列,我們可以使用:usecols=(0,?-1)

>>> data = u"1 2 3\n4 5 6"
>>> np.genfromtxt(StringIO(data), usecols=(0, -1))
array([[ 1.,  3.],
       [ 4.,  6.]])

如果列有名稱,我們還可以通過將名稱作為usecols參數(shù)的名稱作為字符串序列或逗號分隔的字符串來選擇要導(dǎo)入的列:

>>> 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')])

4、選擇數(shù)據(jù)類型

控制我們從文件中讀取的字符串序列如何轉(zhuǎn)換為其他類型的主要方法是設(shè)置dtype參數(shù)。此參數(shù)可接受的值為:

單一類型,例如dtype=float.?輸出將是具有給定 dtype 的 2D,除非使用names參數(shù)將名稱與每列關(guān)聯(lián)(見下文)。請注意,這dtype=float是?genfromtxt.

  • 類型序列,例如.dtype=(int,?float,?float)
  • 逗號分隔的字符串,例如dtype="i4,f8,|U3".
  • 帶有兩個鍵'names'和的字典'formats'。
  • 元組序列,例如?.(name,?type)``dtype=[('A',?int),?('B',?float)]
  • 一個現(xiàn)有的numpy.dtype對象。
  • 特殊價值None。在這種情況下,列的類型將由數(shù)據(jù)本身確定(見下文)。

在除第一種情況之外的所有情況下,輸出將是具有結(jié)構(gòu)化 dtype 的一維數(shù)組。此 dtype 具有與序列中的項目一樣多的字段。字段名稱是用names關(guān)鍵字定義的。

當(dāng) 時dtype=None,每列的類型由其數(shù)據(jù)迭代確定。我們首先檢查字符串是否可以轉(zhuǎn)換為布爾值(即字符串是否匹配truefalse小寫);然后它是否可以轉(zhuǎn)換為整數(shù),然后轉(zhuǎn)換為浮點數(shù),然后轉(zhuǎn)換為復(fù)數(shù),最終轉(zhuǎn)換為字符串。可以通過修改類的默認(rèn)映射器來更改此行為?StringConverter。

dtype=None提供該選項是為了方便。但是,它比顯式設(shè)置 dtype 慢得多。

5、設(shè)置名稱

5.1 names參數(shù)

處理表格數(shù)據(jù)時的一種自然方法是為每一列分配一個名稱。第一種可能性是使用顯式結(jié)構(gòu)化 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關(guān)鍵字與字符串序列或逗號分隔的字符串一起使用:

>>> 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')])

在上面的例子中,我們使用了默認(rèn)情況下,dtype=float.?通過給出一系列名稱,我們將輸出強制為結(jié)構(gòu)化 dtype。

我們有時可能需要從數(shù)據(jù)本身定義列名。在這種情況下,我們必須使用names值為的關(guān)鍵字?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')])

默認(rèn)值namesNone。如果我們給關(guān)鍵字賦予任何其他值,新名稱將覆蓋我們可能用 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')])

5.2 defaultfmt參數(shù)

如果names=None但需要結(jié)構(gòu)化 dtype,則使用標(biāo)準(zhǔn) NumPy 默認(rèn)值定義"f%i"名稱,產(chǎn)生類似 的名稱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 的長度,缺少的名稱將使用這個默認(rèn)模板定義:

>>> 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參數(shù)覆蓋這個默認(rèn)值,它接受任何格式字符串:

>>> 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只有在預(yù)期某些名稱但未定義時才使用它。

5.3 驗證名稱

也可以將具有結(jié)構(gòu)化數(shù)據(jù)類型的 NumPy 數(shù)組視為?recarray,其中可以像訪問屬性一樣訪問字段。出于這個原因,我們可能需要確保字段名稱不包含任何空格或無效字符,或者它不對應(yīng)于標(biāo)準(zhǔn)屬性的名稱(如size或?shape),這會混淆解釋器。?genfromtxt?接受三個可選參數(shù),可以更好地控制名稱:

  • deletechars 給出一個字符串,該字符串組合了必須從名稱中刪除的所有字符。默認(rèn)情況下,無效字符為?.~!@#$%^&*()-=+~\|]}[{';:?/?.>,<
  • excludelist 給出要排除的名稱列表,例如return,?file,?print... 如果輸入名稱之一是此列表的一部分,'_'則將在其后附加下劃線字符 (?)。
  • case_sensitive 名稱是否應(yīng)區(qū)分大小寫 (?case_sensitive=True)、轉(zhuǎn)換為大寫 (case_sensitive=False或?case_sensitive='upper') 或小寫 (?case_sensitive='lower')。

6、調(diào)整轉(zhuǎn)換

6.1 converters參數(shù)

通常,定義 dtype 足以定義必須如何轉(zhuǎn)換字符串序列。然而,有時可能需要一些額外的控制。例如,我們可能希望確保將某種格式的日期?YYYY/MM/DD轉(zhuǎn)換為datetime對象,或者將類似字符串xx%正確轉(zhuǎn)換為介于 0 和 1 之間的浮點數(shù)。在這種情況下,我們應(yīng)該使用converters?參數(shù)定義轉(zhuǎn)換函數(shù)。

此參數(shù)的值通常是一個字典,其中列索引或列名作為鍵,轉(zhuǎn)換函數(shù)作為值。這些轉(zhuǎn)換函數(shù)可以是實際函數(shù)或 lambda 函數(shù)。在任何情況下,它們都應(yīng)該只接受一個字符串作為輸入,并且只輸出所需類型的單個元素。

在以下示例中,第二列從表示百分比的字符串轉(zhuǎn)換為介于 0 和 1 之間的浮點數(shù):

>>> 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')])

我們需要記住,默認(rèn)情況下,dtype=float.?因此,預(yù)計第二列會出現(xiàn)浮點數(shù)。但是,字符串?和不能轉(zhuǎn)換為浮點數(shù),我們最終得到了??,F(xiàn)在讓我們使用轉(zhuǎn)換器:'?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)可以獲得相同的結(jié)果:

>>> # 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')])

轉(zhuǎn)換器還可用于為丟失的條目提供默認(rèn)值。在以下示例中,convert如果字符串為空,轉(zhuǎn)換器將剝離的字符串轉(zhuǎn)換為相應(yīng)的浮點數(shù)或 -999。我們需要從空格中顯式地去除字符串,因為默認(rèn)情況下不這樣做:

>>> 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.]])

6.2 使用缺失值和填充值

我們嘗試導(dǎo)入的數(shù)據(jù)集中可能缺少某些條目。在前面的示例中,我們使用轉(zhuǎn)換器將空字符串轉(zhuǎn)換為浮點數(shù)。然而,用戶定義的轉(zhuǎn)換器可能很快變得難以管理。

genfromtxt函數(shù)提供了另外兩種補充機制:missing_values參數(shù)用于識別缺失數(shù)據(jù),第二個參數(shù)filling_values用于處理這些缺失數(shù)據(jù)。

6.3 missing_values

默認(rèn)情況下,任何空字符串都被標(biāo)記為缺失。我們還可以考慮更復(fù)雜的字符串,例如"N/A""???"來表示丟失或無效的數(shù)據(jù)。該missing_values參數(shù)接受三個類型的值:

  • 字符串或逗號分隔的字符串 此字符串將用作所有列缺失數(shù)據(jù)的標(biāo)記
  • 字符串序列 在這種情況下,每個項目都按順序與一列相關(guān)聯(lián)。
  • 一本字典 字典的值是字符串或字符串序列。對應(yīng)的鍵可以是列索引(整數(shù))或列名(字符串)。此外,特殊鍵None可用于定義適用于所有列的默認(rèn)值。

6.4 filling_values

我們知道如何識別缺失的數(shù)據(jù),但我們?nèi)匀恍枰獮檫@些缺失的條目提供一個值。默認(rèn)情況下,此值是根據(jù)下表根據(jù)預(yù)期的 dtype 確定的:

預(yù)期類型 默認(rèn)
bool False
int -1
float np.nan
complex np.nan+0j
string '???'

我們可以使用filling_values可選參數(shù)更好地控制缺失值的轉(zhuǎn)換?。就像?missing_values,這個參數(shù)接受不同類型的值:

  • 單一值 這將是所有列的默認(rèn)值
  • 值序列 每個條目將是相應(yīng)列的默認(rèn)值
  • 一本字典 每個鍵可以是列索引或列名,對應(yīng)的值應(yīng)該是單個對象。我們可以使用特殊鍵None為所有列定義默認(rèn)值。

在下面的示例中,我們假設(shè)缺失值"N/A"在第一列中標(biāo)記為 with?,"???"在第三列中標(biāo)記為by?。如果它們出現(xiàn)在第一列和第二列,我們希望將這些缺失值轉(zhuǎn)換為 0,如果它們出現(xiàn)在最后一列,則轉(zhuǎn)換為 -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')])

6.5 usemask

我們可能還想通過構(gòu)建一個布爾掩碼來跟蹤丟失數(shù)據(jù)的發(fā)生情況,True其中包含數(shù)據(jù)丟失的條目等False。為此,我們只需將可選參數(shù)設(shè)置usemaskTrue(默認(rèn)為False)。輸出數(shù)組將是一個MaskedArray.

7、快捷功能

此外genfromtxt,該numpy.lib.npyio模塊提供了幾個從?genfromtxt.?這些函數(shù)的工作方式與原始函數(shù)相同,但它們具有不同的默認(rèn)值。

  • recfromtxt 返回標(biāo)準(zhǔn)numpy.recarray(if?usemask=False) 或?MaskedRecords數(shù)組 (if?usemaske=True)。默認(rèn)的 dtype 是dtype=None,這意味著將自動確定每列的類型。
  • recfromcsv 喜歡recfromtxt,但有一個默認(rèn)值delimiter=","
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號