NumPy 導入數據 genfromtxt

2021-09-01 10:06 更新

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

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

1、定義輸入

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

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

2、將行拆分為列

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

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

2.3 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,將檢查第一個注釋行的名稱。

3、跳過行和選擇列

3.1 在skip_headerskip_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=0skip_footer=0,意味著不跳過任何行。

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

4、選擇數據類型

控制我們從文件中讀取的字符串序列如何轉換為其他類型的主要方法是設置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,每列的類型由其數據迭代確定。我們首先檢查字符串是否可以轉換為布爾值(即字符串是否匹配truefalse小寫);然后它是否可以轉換為整數,然后轉換為浮點數,然后轉換為復數,最終轉換為字符串??梢酝ㄟ^修改類的默認映射器來更改此行為?StringConverter

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

5、設置名稱

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

默認值namesNone。如果我們給關鍵字賦予任何其他值,新名稱將覆蓋我們可能用 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參數

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

5.3 驗證名稱

也可以將具有結構化數據類型的 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')。

6、調整轉換

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

6.2 使用缺失值和填充值

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

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

6.3 missing_values

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

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

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

6.5 usemask

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

7、快捷功能

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

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號