[英]numpy.loadtxt: how to ignore comma delimiters that appear inside quotes?
我有一個 csv 文件,其中一行數據可能如下所示:
10,《蘋果、香蕉》,20,...
當我在 Python 中加載數據時,引號內的額外逗號會移動我所有的列索引,因此我的數據不再是一致的結構。 雖然我可能會編寫一個復雜的算法來遍歷每一行並解決問題,但我希望有一種優雅的方法可以將額外的參數傳遞給 loadtxt(或其他一些函數),該參數將正確忽略引號內的逗號並處理整個報價作為一個值。
請注意,當我將 CSV 文件加載到 Excel 中時,Excel 正確地將該字符串識別為一個值。
您可以使用單個 numpy 函數調用來完成的一種方法是使用np.fromregex
,它允許您使用 Python 的正則表達式語法以任意方式解析文本文件的內容。 例如:
np.fromregex('tmp.csv', r'(\d+),"(.+)",(\d+)', np.object)
給你:
array([['10', 'Apple, Banana', '20'],
['30', 'Orange, Watermelon', '40']], dtype=object)
要稍微解壓該正則表達式, '(\\d+)'
將匹配一個或多個數字,而'"(.+)"'
將匹配雙引號內的一個或多個任何字符。 np.fromregex
嘗試在.csv
文件的每一行中匹配這個表達式,括號內的部分被視為輸出數組每一行中的單個元素。
如果您想要一個記錄數組作為.csv
文件中三個“列”的不同“字段”的輸出,您可以為正則表達式中的每組括號指定單獨的數據dtypes
:
np.fromregex('tmp.csv', r'(\d+),"(.+)",(\d+)', 'i8, S20, i8')
給你:
array([(10, 'Apple, Banana', 20), (30, 'Orange, Watermelon', 40)],
dtype=[('f0', '<i8'), ('f1', 'S20'), ('f2', '<i8')])
這個問題之前已經討論過了。 loadtxt
(或genfromtxt
)中沒有可以執行您想要的操作的參數。 換句話說,它對引用不敏感。 python
csv
模塊具有某種報價意識。 pandas
讀者也知道引用。
但是在將它們傳遞給loadtxt
之前處理這些行是完全可以接受的。 所有函數需要的是一個可迭代的——可以一次一行地提供給它的東西。 所以它可以是一個文件、一個行列表或生成器。
一個簡單的處理器只會用其他字符替換引號內的逗號。 或者用您選擇的分隔符替換引號之外的那些。 做這項工作不一定要花哨。
使用 numpy.genfromtxt 讀取包含逗號的字符串的 csv 文件
例如:
txt = """10,"Apple, Banana",20
30,"Pear, Orange",40
50,"Peach, Mango",60
"""
def foo(astr):
# replace , outside quotes with ;
# a bit crude and specialized
x = astr.split('"')
return ';'.join([i.strip(',') for i in x])
txt1 = [foo(astr) for astr in txt.splitlines()]
txtgen = (foo(astr) for astr in txt.splitlines()) # or as generator
# ['10;Apple, Banana;20', '30;Pear, Orange;40', '50;Peach, Mango;60']
np.genfromtxt(txtgen, delimiter=';', dtype=None)
產生:
array([(10, 'Apple, Banana', 20), (30, 'Pear, Orange', 40),
(50, 'Peach, Mango', 60)],
dtype=[('f0', '<i4'), ('f1', 'S13'), ('f2', '<i4')])
我之前沒有關注np.fromregex
。 與genfromtxt
相比,它非常簡單。 要與我的示例txt
一起使用,我必須使用字符串緩沖區:
s=StringIO.StringIO(txt)
np.fromregex(s, r'(\d+),"(.+)",(\d+)', dtype='i4,S20,i4')
它的行動歸結為:
pat=re.compile(r'(\d+),"(.+)",(\d+)'); dt=np.dtype('i4,S20,i4')
np.array(pat.findall(txt),dtype=dt)
它讀取整個文件( f.read()
)並執行findall
,它應該生成如下列表:
[('10', 'Apple, Banana', '20'),
('30', 'Pear, Orange', '40'),
('50', 'Peach, Mango', '60')]
元組列表正是結構化數組所需要的。
沒有花哨的處理、錯誤檢查或注釋行過濾。 只是一個模式匹配,然后是數組構造。
我的foo
和fromregex
都假設特定的數字序列和帶引號的字符串。 csv.reader
可能是最簡單的通用報價閱讀器。 join
是必需的,因為reader
生成一個列表列表,而genfromtxt
想要一個可迭代的字符串(它自己進行“拆分”)。
from csv import reader
s=StringIO.StringIO(txt)
np.genfromtxt((';'.join(x) for x in reader(s)), delimiter=';', dtype=None)
生產
array([(10, 'Apple, Banana', 20), (30, 'Pear, Orange', 40),
(50, 'Peach, Mango', 60)],
dtype=[('f0', '<i4'), ('f1', 'S13'), ('f2', '<i4')])
或者按照fromregex
示例, reader
輸出可以轉換為元組列表並直接提供給np.array
:
np.array([tuple(x) for x in reader(s)], dtype='i4,S20,i4')
我使用下面的代碼解決了這個問題。
def transformCommas(line):
out = ''
insideQuote = False
for c in line:
if c == '"':
insideQuote = not insideQuote
if insideQuote == True and c == ',':
out += '.'
else:
out += c
return out
f = open("data/raw_data_all.csv", "rb")
replaced = (transformCommas(line) for line in f)
rawData = numpy.loadtxt(replaced,delimiter=',', skiprows=0, dtype=str)
數據:
1366x768,18,"5,237",73.38%,"3,843",79.55%,1.75,00:01:26,4.09%,214,$0.00
1366x768,22,"5,088",76.04%,"3,869",78.46%,1.82,00:01:20,3.93%,200,$0.00
1366x768,17,"4,887",74.34%,"3,633",78.37%,1.81,00:01:19,3.25%,159,$0.00
您可以使用 Python csv模塊: https : //docs.python.org/2/library/csv.html
給定一個 csv 格式的數據文件:
10,"Apple,Banana",20
20,"Orange,Watermelon",30
使用這個腳本:
from csv import reader
with open('data.csv') as f:
for row in reader(f):
print row
你得到:
['10', 'Apple,Banana', '20']
['20', 'Orange,Watermelon', '30']
由於 loadtxt 需要一個可迭代對象,請將其傳遞給reader(f)
:
with open('data.csv') as f:
data = loadtxt(reader(f), ...)
雖然 numpy.loadtxt 中沒有這樣的參數來忽略引用或以其他方式轉義的逗號,但尚未建議的一種替代方法是以下...
使用一些文本編輯器執行查找和替換,用制表符替換逗號或將文件保存在 Excel 中作為制表符分隔。
當您使用 numpy.loadtxt 時,您只需指定delimiter='\\t'
而不是逗號分隔。
可以為您節省一些代碼的簡單解決方案......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.