读取以第一列为键,其余为值的制表符分隔文件
Posted
技术标签:
【中文标题】读取以第一列为键,其余为值的制表符分隔文件【英文标题】:Read a tab separated file with first column as key and the rest as values 【发布时间】:2015-07-07 08:44:12 【问题描述】:我有一个制表符分隔文件,其中包含 10 亿行(想象一下 200 列,而不是 3 列):
abc -0.123 0.6524 0.325
foo -0.9808 0.874 -0.2341
bar 0.23123 -0.123124 -0.1232
我想创建一个字典,其中第一列中的字符串是键,其余的是值。我一直在这样做,但计算量很大:
import io
dictionary =
with io.open('bigfile', 'r') as fin:
for line in fin:
kv = line.strip().split()
k, v = kv[0], kv[1:]
dictionary[k] = list(map(float, v))
我还能如何获得所需的字典?实际上,numpy 数组比浮点数列表更适合该值。
【问题讨论】:
【参考方案1】:您可以使用pandas加载df,然后根据需要构造一个新的df然后调用to_dict
:
In [99]:
t="""abc -0.123 0.6524 0.325
foo -0.9808 0.874 -0.2341
bar 0.23123 -0.123124 -0.1232"""
df = pd.read_csv(io.StringIO(t), sep='\s+', header=None)
df = pd.DataFrame(columns = df[0], data = df.ix[:,1:].values)
df.to_dict()
Out[99]:
'abc': 0: -0.12300000000000001,
1: -0.98080000000000001,
2: 0.23123000000000002,
'bar': 0: 0.32500000000000001, 1: -0.2341, 2: -0.1232,
'foo': 0: 0.65239999999999998, 1: 0.87400000000000011, 2: -0.123124
编辑
一种更动态的方法,可以减少构建临时 df 的需要:
In [121]:
t="""abc -0.123 0.6524 0.325
foo -0.9808 0.874 -0.2341
bar 0.23123 -0.123124 -0.1232"""
# determine the number of cols, we'll use this in usecols
col_len = pd.read_csv(io.StringIO(t), sep='\s+', nrows=1).shape[1]
col_len
# read the first col we'll use this in names
cols = pd.read_csv(io.StringIO(t), sep='\s+', usecols=[0], header=None)[0].values
# now read and construct the df using the determined usecols and names from above
df = pd.read_csv(io.StringIO(t), sep='\s+', header=None, usecols = list(range(1, col_len)), names = cols)
df.to_dict()
Out[121]:
'abc': 0: -0.12300000000000001,
1: -0.98080000000000001,
2: 0.23123000000000002,
'bar': 0: 0.32500000000000001, 1: -0.2341, 2: -0.1232,
'foo': 0: 0.65239999999999998, 1: 0.87400000000000011, 2: -0.123124
进一步更新
其实你不需要第一次读取,列长反正可以通过第一列的列数隐式推导出来:
In [128]:
t="""abc -0.123 0.6524 0.325
foo -0.9808 0.874 -0.2341
bar 0.23123 -0.123124 -0.1232"""
cols = pd.read_csv(io.StringIO(t), sep='\s+', usecols=[0], header=None)[0].values
df = pd.read_csv(io.StringIO(t), sep='\s+', header=None, usecols = list(range(1, len(cols)+1)), names = cols)
df.to_dict()
Out[128]:
'abc': 0: -0.12300000000000001,
1: -0.98080000000000001,
2: 0.23123000000000002,
'bar': 0: 0.32500000000000001, 1: -0.2341, 2: -0.1232,
'foo': 0: 0.65239999999999998, 1: 0.87400000000000011, 2: -0.123124
【讨论】:
然后我可以通过df['foo'].values()
来获得按功能键排序的列表吗?或者values()
会打乱列的顺序吗?
值总是按顺序排列的
呃,但是动态方法将读取十亿行 csv 文件三次?还是我在这里遗漏了什么?
@JohnGalt nope 第一次读取它只读取一行以确定列数,第二次读取它只读取列,第三次读取只读取数据
@EdChum 非常好 - 我的错,没有清楚地看穿线条。道歉!【参考方案2】:
如果您指定列数,您可以使用numpy.genfromtxt()
函数:
import numpy as np
a = np.genfromtxt('bigfile.csv',dtype=str,usecols=(0))
b = np.genfromtxt('bigfile.csv',dtype=float,delimiter='\t',usecols=range(1,4))
#^enter # of cols here
d = dict(zip(a,b.tolist())) #if you want a numpy array, just remove .tolist()
print d
输出:
'abc': [-0.123, 0.6524, 0.325], 'bar': [0.23123, -0.123124, -0.1232], 'foo': [-0.9808, 0.874, -0.2341]
注意:要以编程方式查找 cols
的数量,您可以这样做:
with open('bigfile.csv', 'r') as f:
num_cols = len(f.readline().split())
然后使用num_cols
作为usecols
参数。
【讨论】:
我从b = np.genfromtxt('bigfile',dtype='float',delimiter='\t',usecols=range(1,401)
得到Line #1 (got 1 columns instead of 400)
400 是列数。
@alvas 你确定实际上有 400 列由'\t'
分隔吗?另外,您不需要在float
周围引用
@alvas 您还应该为'bigfile'
添加一个扩展名,例如'bigfile.csv'
这很奇怪。同样的got 1 columns
发生在b = np.genfromtxt('bigfile.csv',dtype=float,delimiter='\t',usecols=range(1,401))
上。它肯定是制表符分隔的,因为我可以在 unix 上执行 cut -f
并且肯定是来自 awk 'print NF;quit' bigfile.csv
的 401 列
@alvas 我刚刚尝试重现您的错误,它仅在恰好有 1 列时显示。老实说,我不知道有什么问题。如果您让我们查看文件会有所帮助,但我了解隐私问题。【参考方案3】:
您可以使用csv
模块来读取文件,以便拆分行,然后使用np.array
将浮点值转换为 numpy 数组对象:
import csv
import numpy as np
dictionary =
with open('bigfile.csv', 'rb') as csvfile:
spamreader = csv.reader(csvfile, delimiter='\t',)
for row in spamreader:
k, v = row[0], row[1:] #in python3 do k,*v = row
dictionary[k] = np.array(map(float, v))
【讨论】:
熊猫会更快或更高效吗? @alvas 对不起,我不熟悉熊猫!所以我不知道! 我想听听投反对票的原因,如果我错了,我可以编辑我的答案并改进它! 不是我 ;P . @alvas 我不知道为什么以及如何一些人无缘无故地否决答案!【参考方案4】:使用Pandas
的一种方式。假设你这样做 df = pd.read_csv(file)
和 df
就像
In [220]: df
Out[220]:
k a1 a2 a3
0 abc -0.12300 0.652400 0.3250
1 foo -0.98080 0.874000 -0.2341
2 bar 0.23123 -0.123124 -0.1232
我添加了虚拟列名,您可以在读取 csv 文件时灵活地更改它
那么您可以执行以下操作。
In [221]: df.set_index('k').T.to_dict('list')
Out[221]:
'abc': [-0.12300000000000001, 0.65239999999999998, 0.32500000000000001],
'bar': [0.23123000000000002, -0.123124, -0.1232],
'foo': [-0.98080000000000001, 0.87400000000000011, -0.2341]
【讨论】:
【参考方案5】:对不起,这不是一个真正的答案,但评论太长了。
你说你有 10 亿行和 200 列浮点数。这意味着
的最小内存109 * 200 * 8 = 1.6 1012 字节
不计算字典的开销,它提供了超过 1.5 G。
当然,您可以尝试使用numpy
数组而不是浮点数列表,但是每个数组都很小(200 个元素),所以我非常怀疑增益是否重要。
恕我直言,对于这么多数据,您不应该独立于处理数据的方式来考虑加载阶段,如果您真的需要一个包含 10 亿条记录的字典,每条记录有 200 个浮点值,您当前实现是正确的,numpy 数组也是正确的。
如果您可以将所有数据放在一个 numpy 数组中,并且确实使用 numpy 作为处理部分,那么您可以在进一步处理中获得重要收益,但在不了解更多信息的情况下,这只是推测。
【讨论】:
以上是关于读取以第一列为键,其余为值的制表符分隔文件的主要内容,如果未能解决你的问题,请参考以下文章