将制表符分隔的 csv 读入具有不同数据类型的 numpy 数组

Posted

技术标签:

【中文标题】将制表符分隔的 csv 读入具有不同数据类型的 numpy 数组【英文标题】:Reading tab delimited csv into numpy array with different data types 【发布时间】:2017-03-04 17:05:06 【问题描述】:

我有一个制表符分隔的 csv 数据集,如下所示:

1       2       3       4       5       6       [0, 1, 2, 3, 4, 5]
3       1       2       6       4       5       [2, 0, 1, 5, 3, 4]
7       8       9       10      11      6       [0, 1, 2, 3, 4, 5]
10      11      9       8       7       6       [3, 4, 2, 1, 0, 5]
12      13      4       14      15      6       [0, 1, 2, 3, 4, 5]
13      4       14      12      15      6       [1, 2, 3, 0, 4, 5]
16      17      18      19      20      6       [0, 1, 2, 3, 4, 5]
6       18      20      17      16      19      [5, 2, 4, 1, 0, 3]
7       21      22      23      24      6       [0, 1, 2, 3, 4, 5]
23      6       21      7       22      24      [3, 5, 1, 0, 2, 4]
25      7       21      22      23      6       [0, 1, 2, 3, 4, 5]
6       21      7       22      25      23      [5, 2, 1, 3, 0, 4]
16      26      3       27      28      6       [0, 1, 2, 3, 4, 5]
26      6       27      3       28      16      [1, 5, 3, 2, 4, 0]
7       29      24      30      31      6       [0, 1, 2, 3, 4, 5]
30      24      6       7       29      31      [3, 2, 5, 0, 1, 4]
32      33      13      34      35      36      [0, 1, 2, 3, 4, 5]
34      32      36      35      13      33      [3, 0, 5, 4, 2, 1]
7       37      38      39      40      6       [0, 1, 2, 3, 4, 5]
39      38      40      6       37      7       [3, 2, 4, 5, 1, 0]
7       41      42      43      44      6       [0, 1, 2, 3, 4, 5]
41      6       44      43      42      7       [1, 5, 4, 3, 2, 0]
7       45      46      47      48      6       [0, 1, 2, 3, 4, 5]
6       47      45      7       46      48      [5, 3, 1, 0, 2, 4]
49      2       50      51      52      6       [0, 1, 2, 3, 4, 5]

当我想将这样的 csv 文件导入 numpy 数组时,如下所示;

dataset = numpy.loadtxt('dataset/demo_dataset.csv', delimiter='\t', dtype='str')

我获得了一个具有(25,) 形状的 numpy 数组。

我想将此 csv 文件导入两个 numpy 数组,分别称为 X 和 Y。

X 将包括前 6 列,Y 将包括最后一列作为列表值,而不是 str。

我该如何管理它?

【问题讨论】:

将 6 列加载为整数,最后一列加载为字符串很容易。 dtype=None 应该这样做,或者更精致的自定义 dtype。但是[] 字符串需要特殊处理,在genfromtxt 之前或之后。 【参考方案1】:

我设法通过自定义方法实现了这一点:

import numpy

with open('dataset/demo_dataset.csv', 'r') as fin:
    lines = fin.readlines()
    # remove '\n' characters
    clean_lines = [l.strip('\n') for l in lines]
    # split on tab so that we get lists from strings
    A = [cl.split('\t') for cl in clean_lines]
    # get lists of ints instead of lists of strings
    X = [map(int, row[0:6]) for row in A]
    # last column in Y
    Y = [row[6] for row in A]

    # convert string to int values
    for i in xrange(len(Y)):
        Y[i] = map(int, Y[i].strip('[]').split(','))

【讨论】:

【参考方案2】:

一些使用genfromtxt的选项:

In [1047]: txt=b"""7\t8\t9\t10\t11\t6\t [0, 1, 2, 3, 4, 5]"""
In [1048]: txt=[txt,txt,txt]
In [1049]: txt
Out[1049]: 
[b'7\t8\t9\t10\t11\t6\t [0, 1, 2, 3, 4, 5]',
 b'7\t8\t9\t10\t11\t6\t [0, 1, 2, 3, 4, 5]',
 b'7\t8\t9\t10\t11\t6\t [0, 1, 2, 3, 4, 5]']

加载为默认浮点数 - 最后一列是nan

In [1050]: np.genfromtxt(txt,delimiter='\t')
Out[1050]: 
array([[  7.,   8.,   9.,  10.,  11.,   6.,  nan],
       [  7.,   8.,   9.,  10.,  11.,   6.,  nan],
       [  7.,   8.,   9.,  10.,  11.,   6.,  nan]])

作为字符串

In [1051]: np.genfromtxt(txt,delimiter='\t',dtype='str')
Out[1051]: 
array([['7', '8', '9', '10', '11', '6', ' [0, 1, 2, 3, 4, 5]'],
       ['7', '8', '9', '10', '11', '6', ' [0, 1, 2, 3, 4, 5]'],
       ['7', '8', '9', '10', '11', '6', ' [0, 1, 2, 3, 4, 5]']], 
      dtype='<U19')

让它决定最合适的 - 结果是一个结构化数组,具有 int 字段和一个字符串字段。

In [1052]: np.genfromtxt(txt,delimiter='\t',dtype=None)
Out[1052]: 
array([(7, 8, 9, 10, 11, 6, b' [0, 1, 2, 3, 4, 5]'),
       (7, 8, 9, 10, 11, 6, b' [0, 1, 2, 3, 4, 5]'),
       (7, 8, 9, 10, 11, 6, b' [0, 1, 2, 3, 4, 5]')], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4'), ('f3', '<i4'), ('f4', '<i4'), ('f5', '<i4'), ('f6', 'S19')])
In [1053]: _['f6']
Out[1053]: 
array([b' [0, 1, 2, 3, 4, 5]', b' [0, 1, 2, 3, 4, 5]',
       b' [0, 1, 2, 3, 4, 5]'], 
      dtype='|S19')

优化 dtype - 一个字段有 6 列,另一个是字符串:

In [1055]: np.genfromtxt(txt,delimiter='\t',dtype='6int,S20')
Out[1055]: 
array([([7, 8, 9, 10, 11, 6], b' [0, 1, 2, 3, 4, 5]'),
       ([7, 8, 9, 10, 11, 6], b' [0, 1, 2, 3, 4, 5]'),
       ([7, 8, 9, 10, 11, 6], b' [0, 1, 2, 3, 4, 5]')], 
      dtype=[('f0', '<i4', (6,)), ('f1', 'S20')])

第一个字段是您想要的X;最后一个字段中的字符串需要进一步处理(根据您的其他问题):

In [1060]: _['f0']
Out[1060]: 
array([[ 7,  8,  9, 10, 11,  6],
       [ 7,  8,  9, 10, 11,  6],
       [ 7,  8,  9, 10, 11,  6]])
In [1061]: __['f1']
Out[1061]: 
array([b' [0, 1, 2, 3, 4, 5]', b' [0, 1, 2, 3, 4, 5]',
       b' [0, 1, 2, 3, 4, 5]'], 

最后一个字段可以像 @chefarovY 变量一样转换。

想一想,我可以通过再次调用genfromtxt 来处理该字符串字段。我仍然需要删除[]

In [1101]: data=np.genfromtxt(txt,delimiter='\t',dtype='6int,S20')
In [1102]: data['f1']
Out[1102]: 
array([b'[0, 1, 2, 3, 4, 5]', b'[0, 1, 2, 3, 4, 5]', b'[0, 1, 2, 3, 4, 5]'], 
      dtype='|S20')
In [1103]: np.genfromtxt([l.strip(b'[]') for l in data['f1']],delimiter=',',dtype=int)
Out[1103]: 
array([[0, 1, 2, 3, 4, 5],
       [0, 1, 2, 3, 4, 5],
       [0, 1, 2, 3, 4, 5]])

在其他情况下,我建议对行进行预处理以删除引号、括号和问题分隔符。 genfromtxt 接受来自任何提供它的线路的输入。但在这种情况下,所需的处理是@chefarov 建议的大部分 - 减去int 转换。

genfromtxt 也接受转换器,但我在其他问题中发现转换器无法将一个字段更改为多个字段。

还有genfromtxt 迭代文件行,解码每个行并收集列表中的值。所以它对于自定义阅读器没有任何速度优势。

【讨论】:

【参考方案3】:

如果您使用pandas,则有一种(可以说)更简单的方法来读取数据。首先,可以构造一个pandas.DataFrame 实例,我们可以将自定义函数应用于其最后一列,以将字符串元素转换为np.ndarray 类型:

import pandas as pd
import numpy as np

df = pd.read_table('dataset/demo_dataset.csv', delimiter='\t', names='abcdefg')
convert = lambda a: np.fromstring(a[1:-1], count = a.count(',') + 1, sep = ', ', dtype=int)
df.g = df.g.apply(convert)

一旦构建了混合数据框,XY 可以以简单的方式提取为数组:

X = df.values[:, :-1].astype(int)
Y = np.vstack(df.values[:, -1])

【讨论】:

以上是关于将制表符分隔的 csv 读入具有不同数据类型的 numpy 数组的主要内容,如果未能解决你的问题,请参考以下文章

使用 Pandas 将 CSV 读入具有不同行长的数据帧

在 Ruby 中将数组输出到 CSV

使用 phpMyAdmin 将带有部分数据的制表符分隔的 csv 文件导入 mysql 表

无法将制表符分隔的文件读入 numpy 二维数组

如何将 CSV 或制表符分隔文件映射到 MySQL 多表数据库

Spring Boot下的一种导出CSV文件的代码框架