从csv文件创建矩阵 - Python

时间:2015-08-29 09:47:47

标签: python parsing csv matrix

我试图从.csv文件中读取一些数字并使用Python将它们存储到矩阵中。输入文件如下所示

  

输入文件

B,1
A,1
A,1
B,1
A,3
A,2
B,1
B,2
B,2

输入将被操作为像

这样的矩阵
  

输出文件

  1 2 3 
A 2 1 1
B 3 2 0

这里,输入文件的第一列成为行,第二列成为列,值是发生的计数。我该如何实现呢?我输入文件的大小很大(1000000行),因此可以有大量行(50到10,000之间)和列(从1到50)

5 个答案:

答案 0 :(得分:1)

使用pandas,它变得简单,几乎只有3行

import pandas as pd

df = pd.read_csv('example.csv', names=['label', 'value'])
# >>> df
#   label  value
# 0     B      1
# 1     A      1
# 2     A      1
# 3     B      1
# 4     A      3
# 5     A      2
# 6     B      1
# 7     B      2
# 8     B      2

s = df.groupby(['label', 'value']).size()
# >>> s
# label  value
# A      1        2
#        2        1
#        3        1
# B      1        3
#        2        2
# dtype: int64

# ref1: http://stackoverflow.com/questions/15751283/converting-a-pandas-multiindex-dataframe-from-rows-wise-to-column-wise
# ref2: http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.unstack.html
m = s.unstack()
# >>> m
# value  1  2   3
# label          
# A      2  1   1
# B      3  2 NaN

# Below are optional: just to make it look more like what you want
m.columns.name = None
m.index.name = None
m = m.fillna(0)
print m
#    1  2  3
# A  2  1  1
# B  3  2  0

答案 1 :(得分:0)

以下解决方案仅使用标准Python模块:

import csv, collections, itertools

with open('my.csv', 'r') as f_input:
    counts = collections.Counter()
    for cols in csv.reader(f_input):
        counts[(cols[0], cols[1])] += 1

keys = set(key[0] for key in counts.keys())
values = set(counts.values())

d = {}
for k in itertools.product(keys, values):
    d[(k[0], str(k[1]))] = 0

d.update(dict(counts))

with open('output.csv', 'wb') as f_output:
    csv_output = csv.writer(f_output)

    # Write the header, 'X' is whatever you want the first column called
    csv_output.writerow(['X'] + sorted(values))

    # Write the rows
    for k, g in itertools.groupby(sorted(d.items()), key=lambda x: x[0][0]):
        csv_output.writerow([k] + [col[1] for col in g])

这为您提供了一个输出CSV文件,如下所示:

X,1,2,3
A,2,1,1
B,3,2,0

答案 2 :(得分:0)

我的解决方案对于大量的输入数据似乎并不是非常有效,因为我手动执行了大量的操作,可能是由一些pandas DataFrame方法完成的。

但是,这可以完成这项任务:

#!/usr/bin/env python3
# coding: utf-8

import pandas as pd
from collections import Counter

with open('foo.txt') as f:
     l = f.read().splitlines()

numbers_list = []
letters_list = []

for element in l:
    letter = element.split(',')[0]
    number = element.split(',')[1]
    if number not in numbers_list:
        numbers_list.append(number)
    if letter not in letters_list:
        letters_list.append(letter)

c = Counter(l)
d = dict(c)

output = pd.DataFrame(columns=sorted(numbers_list), index=sorted(letters_list))

for col in numbers_list:
    for row in letters_list:
        key = '{},{}'.format(row, col)
        if key in d:
            output[col][row] = d[key]
        else:
            output[col][row] = 0

输出符合要求:

   1  2  3
A  2  1  1
B  3  2  0

答案 3 :(得分:0)

以下是使用标准模块的另一种变体:

import csv
import re
from collections import defaultdict
from itertools import chain

d = defaultdict(list)

with open('data.csv', 'rb') as f:
    reader = csv.reader(f, delimiter=',')
    for row in reader:
        d[row[0]].append(row[1])

k = sorted(d.keys())
v = sorted(map(int,set(chain.from_iterable(d.values()))))

e = []
for i in d:
    e.append([0]*len(v))
    for j in d[i]:
        e[-1][int(j)-1] += 1

print ' ', re.sub(r'[\[\],]','',str(v))
for i, j in enumerate(k):
    print j, re.sub(r'[\[\],]','',str(e[i]))

鉴于data.csv具有问题中显示的输入文件的内容,此脚本将输出以下内容作为输出:

  1 2 3
A 2 1 1
B 3 2 0

感谢@zyxue提供的纯熊猫解决方案。由于选择它的问题,预先需要的代码少得多。但是,对于运行时性能而言,额外编码并不一定是徒劳的。使用IPython中的timeit来测量我的代码和使用纯大熊猫的& zyxue之间的运行时间差异,我发现我的方法运行速度快了36倍,不包括导入和输入IO,并且在执行输出IO时也快了121倍(打印语句) 。这些测试是通过封装代码块的函数完成的。以下是使用Python 2.7.10和Pandas 0.16.2测试的函数:

def p(): # 1st pandas function
    s = df.groupby(['label', 'value']).size()
    m = s.unstack()
    m.columns.name = None
    m.index.name = None
    m = m.fillna(0)
    print m

def p1(): # 2nd pandas function - omitting print statement
    s = df.groupby(['label', 'value']).size()
    m = s.unstack()
    m.columns.name = None
    m.index.name = None
    m = m.fillna(0)

def q(): # first std mods function
    k = sorted(d.keys())
    v = sorted(map(int,set(chain.from_iterable(d.values()))))
    e = []
    for i in d:
        e.append([0]*len(v))
        for j in d[i]:
            e[-1][int(j)-1] += 1       
    print ' ', re.sub(r'[\[\],]','',str(v))
    for i, j in enumerate(k):
        print j, re.sub(r'[\[\],]','',str(e[i]))

def q1(): # 2nd std mods function - omitting print statements
    k = sorted(d.keys())
    v = sorted(map(int,set(chain.from_iterable(d.values()))))
    e = []
    for i in d:
        e.append([0]*len(v))
        for j in d[i]:
            e[-1][int(j)-1] += 1

在测试之前,运行以下代码来导入模块,输入IO并初始化所有函数的变量:

import pandas as pd
df = pd.read_csv('data.csv', names=['label', 'value'])

import csv
from collections import defaultdict
from itertools import chain
import re

d = defaultdict(list)

with open('data.csv', 'rb') as f:
    reader = csv.reader(f, delimiter=',')
    for row in reader:
        d[row[0]].append(row[1])

data.csv输入文件的内容是:

B,1
A,1
A,1
B,1
A,3
A,2
B,1
B,2
B,2

每个函数的测试命令行的格式为:

%timeit fun()

以下是测试结果:

p():100个循环,最佳3:每循环4.47毫秒

p1():1000次循环,最好3次:每循环1.88 ms

q():10000次循环,最佳3:每循环123μs

q1():100000个循环,最佳3:每循环15.5μs

这些结果仅供参考,适用于一个小型数据集。特别值得一提的是,对于大型数据集而言,我认为大熊猫的表现要好一些。

答案 4 :(得分:0)

以下是使用Hadoop流式处理MapReduce的方法,其中mapper和reducer脚本都读取stdin。

映射器脚本主要是一种输入机制,并过滤输入以删除不正确的数据,其优点是输入可以在多个映射器进程上分割,总输出自动排序并转发到reducer以及在mapper上本地运行组合器的可能性节点。组合器本质上是中间减速器,可用于通过群集上的并行性来加速减少。

# mapper script
import sys
import re

# mapper
for line in sys.stdin:
    line = line.strip()
    word = line.split()[0]
    if word and re.match(r'\A[a-zA-Z]+,[0-9]+',word):
        print '%s\t%s' % (word)

reducer脚本在所有映射器上获得排序输出,为每个输入键(如A或B)构建一个中间字典,在代码中称为“前缀”,并将结果输出为csv格式的文件。

# reducer script
from collections import defaultdict 
import sys

def output(s,d):
    """
    this function takes a string s and dictionary d with int keys and values
    and sorts the keys then creates a string of comma-separate values ordered 
    by the keys with appropriate insertion of comma-separate zeros equal in 
    number to the difference between successive keys minus one
    """
    v = sorted(d.keys())
    o = str(s) + ','
    lastk = 0
    for k in v:
        o += '0,'*(k-lastk-1) + str(d[k]) + ','
        lastk = k
    return o

prefix = ''
current_prefix = ''
d = defaultdict(int)
maxkey = 0

for line in sys.stdin:
    line = line.strip()
    prefix,value = line.split(',')

    try:
        value = int(value)
    except ValueError:
        continue

    if current_prefix == prefix:
        d[value] += 1
    else:
        if current_prefix:
            if len(d) > 0:
                print output(current_prefix,d)
                t = max(d.keys())
                if t > maxkey:
                    maxkey = t
            d = defaultdict(int)
        current_prefix = prefix                   
        d[value] += 1       

# output info for last prefix if needed
if current_prefix == prefix:
    print output(prefix,d)
    t = max(d.keys())
    if t > maxkey:
        maxkey = t    

# output csv list of keys from 1 through maxkey
h = ' ,'
for i in range(1,maxkey+1):
    h += str(i) + ','
print h

要运行数据流过程,只要映射器得到:

B,1
A,1
A,1
B,1
A,3
A,2
B,1
B,2
B,2

它直接输出相同的内容,然后所有内容都被排序(混洗)并发送到reducer。在这个例子中,reducer得到的是:

A,1
A,1
A,2
A,3
B,1
B,1
B,1
B,2
B,2

最后,减速器的输出是:

A,2,1,1,
B,3,2,
 ,1,2,3,    

对于较大的数据集,输入文件将被拆分,其中包含用于某些键集的所有数据的部分将转到单独的映射器。在每个映射器节点上使用组合器将节省整体排序时间。仍然需要一个减速器,以便输出完全按键排序。如果这不是必需的,可以使用多个减速器。

出于实际原因,我做了几个选择。首先,每行输出仅上升到键的最高整数,并且不打印尾随零,因为在处理完所有输入之前无法知道要写入多少,对于大输入意味着存储大量内存中的中间数据或通过将其写入磁盘并将其重新读入以完成作业来减慢处理速度。其次,出于同样的原因,标题行直到reduce作业结束之前才能写入,所以当它写入时。可以将其添加到输出文件中,或者如果输出已经拆分则可以将其添加到第一个输出文件中,并且可以在适当的时候进行调查。但是,通过并行处理提供了极大的性能提升,对于大量输入,这些都是小问题。

此方法适用于Spark群集上相对较小但至关重要的修改,并且可以转换为Java或Scala以在必要时提高性能。