有效显示堆积条形图

时间:2013-09-09 20:49:16

标签: python memory graph matplotlib bar-chart

n 可能出现在 m 不同时间的唯一事件:

time    event
0       A
1       A C
2       A B
3       A
4       B C
5       B C
6       A
7       B

事件发生的次数记录存储在一组 n 矢量中 m

A vector: {1,2,3,4,4,4,5,5}
B vector: {0,0,1,1,2,3,3,4}
C vector: {0,1,1,1,2,3,3,3}

我想知道的是如何以堆叠条形图的形式有效地显示向量。我尝试了matplotlib(有很少的python经验)并遵循这个例子:http://matplotlib.org/examples/pylab_examples/bar_stacked.html

我确实得到了条形图,但程序使用的内存量太多了。在我的程序中,我有11个事件向量,每个向量大小约为25000。出于某种原因,该应用程序将使用超过5GB的内存。

问题可能是我编写脚本的方式还是python只是滥用内存?如果能够更好地完成工作,我也很乐意使用Mathematica或MATLAB。


编辑1

以下是一些有效的代码:

#!/usr/bin/env python
# a stacked bar plot with errorbars
import numpy as np
import matplotlib.pyplot as plt
import sys, string, os

# Initialize time count
nTimes = 0

# Initialize event counts
nA = 0
nB = 0
nC = 0
nD = 0
nE = 0
nF = 0
nG = 0
nH = 0
nI = 0
nJ = 0
nK = 0

# Initialize event vectors
A_Vec = []
B_Vec = []
C_Vec = []
D_Vec = []
E_Vec = []
F_Vec = []
G_Vec = []
H_Vec = []
I_Vec = []
J_Vec = []
K_Vec = []

# Check for command-line argument
if (len(sys.argv) < 2):
    exit()

# Open file
with open(sys.argv[1]) as infile:
    # For every line in the data file...
    for line in infile:
        # Split up tokens
        tokens = line.split(" ")
        # Get the current time
        cur_time = int(tokens[1])

        # Fill in in-between values
        for time in range(len(A_Vec),cur_time):
            A_Vec.append(nA)
            B_Vec.append(nB)
            C_Vec.append(nC)
            D_Vec.append(nD)
            E_Vec.append(nE)
            F_Vec.append(nF)
            G_Vec.append(nG)
            H_Vec.append(nH)
            I_Vec.append(nI)
            J_Vec.append(nJ)
            K_Vec.append(nK)

        # Figure add event type and add result
        if (tokens[2] == 'A_EVENT'):
            nA += 1
        elif (tokens[2] == 'B_EVENT'):
            nB += 1
        elif (tokens[2] == 'C_EVENT'):
            nC += 1
        elif (tokens[2] == 'D_EVENT'):
            nD += 1
        elif (tokens[2] == 'E_EVENT'):
            nE += 1
        elif (tokens[2] == 'F_EVENT'):
            nF += 1
        elif (tokens[2] == 'G_EVENT'):
            nG += 1
        elif (tokens[2] == 'H_EVENT'):
            nH += 1
        elif (tokens[2] == 'I_EVENT'):
            nI += 1
        elif (tokens[2] == 'J_EVENT'):
            nJ += 1
        elif (tokens[2] == 'K_EVENT'):
            nK += 1

        if(cur_time == nTimes):
            A_Vec[cur_time] = nA
            B_Vec[cur_time] = nB
            C_Vec[cur_time] = nC
            D_Vec[cur_time] = nD
            E_Vec[cur_time] = nE
            F_Vec[cur_time] = nF
            G_Vec[cur_time] = nG
            H_Vec[cur_time] = nH
            I_Vec[cur_time] = nI
            J_Vec[cur_time] = nJ
            K_Vec[cur_time] = nK
        else:
            A_Vec.append(nA)
            B_Vec.append(nB)
            C_Vec.append(nC)
            D_Vec.append(nD)
            E_Vec.append(nE)
            F_Vec.append(nF)
            G_Vec.append(nG)
            H_Vec.append(nH)
            I_Vec.append(nI)
            J_Vec.append(nJ)
            K_Vec.append(nK)
        # Update time count
        nTimes = cur_time

# Set graph parameters
ind = np.arange(nTimes+1)
width = 1.00
vecs = [A_Vec,B_Vec,C_Vec,D_Vec,E_Vec,F_Vec,G_Vec,H_Vec,I_Vec,J_Vec,K_Vec]
tmp_accum = np.zeros(len(vecs[0]))

# Create bars
pA      =   plt.bar(ind, A_Vec, color='#848484',    edgecolor = "none", width=1)
tmp_accum += vecs[0]
pB      =   plt.bar(ind, B_Vec, color='#FF0000',    edgecolor = "none", width=1,    bottom=tmp_accum)
tmp_accum += vecs[1]
pC      =   plt.bar(ind, C_Vec, color='#04B404',    edgecolor = "none", width=1,    bottom=tmp_accum)
tmp_accum += vecs[2]
pD      =   plt.bar(ind, D_Vec, color='#8904B1',    edgecolor = "none", width=1,    bottom=tmp_accum)
tmp_accum += vecs[3]
pE      =   plt.bar(ind, E_Vec, color='#FFBF00',    edgecolor = "none", width=1,    bottom=tmp_accum)
tmp_accum += vecs[4]
pF      =   plt.bar(ind, F_Vec, color='#FF0080',    edgecolor = "none", width=1,    bottom=tmp_accum)
tmp_accum += vecs[5]
pG      =   plt.bar(ind, G_Vec, color='#0404B4',    edgecolor = "none", width=1,    bottom=tmp_accum)
tmp_accum += vecs[6]
pH      =   plt.bar(ind, H_Vec, color='#E2A9F3',    edgecolor = "none", width=1,    bottom=tmp_accum)
tmp_accum += vecs[7]
pI      =   plt.bar(ind, I_Vec, color='#A9D0F5',    edgecolor = "none", width=1,    bottom=tmp_accum)
tmp_accum += vecs[8]
pJ      =   plt.bar(ind, J_Vec, color='#FFFF00',    edgecolor = "none", width=1,    bottom=tmp_accum)
tmp_accum += vecs[9]
pK      =   plt.bar(ind, K_Vec, color='#58ACFA',    edgecolor = "none", width=1,    bottom=tmp_accum)

# Add up event count
nEvents = nA+nB+nC+nD+nE+nF+nG+nH+nI+nJ+nK
print 'nEvents = ' + str(nEvents)
# Add graph labels
plt.title('Events/Time Count')
plt.xlabel('Times')
plt.xticks(np.arange(0, nTimes+1, 1))
plt.ylabel('# of Events')
plt.yticks(np.arange(0,nEvents,1))
plt.legend( (pA[0],pB[0],pC[0],pD[0],pE[0],pF[0],pG[0],pH[0],pI[0],pJ[0],pK[0]), ('A','B','C','D','E','F','G','H','I','J','K') , loc='upper left')

plt.show()

以下是输入文件的示例:

TIME 5 A_EVENT 
TIME 6 B_EVENT 
TIME 6 C_EVENT 
TIME 7 A_EVENT 
TIME 7 A_EVENT 
TIME 7 D_EVENT 
TIME 8 E_EVENT 
TIME 8 J_EVENT 
TIME 8 A_EVENT 
TIME 8 A_EVENT 

结果如下: enter image description here

程序执行方式如下:python tally_events.py input.txt


编辑2

import numpy as np
from itertools import cycle
from collections import defaultdict
from matplotlib import pyplot as plt
import sys, string, os

# Check for command-line argument
if (len(sys.argv) < 2):
    exit()

# Get values from input file
d = defaultdict(lambda : [0]*100000)
with open(sys.argv[1], 'r') as infile:
    for line in infile:
        tokens = line.rstrip().split(" ")
        time = int(tokens[1])
        event = tokens[2]
        d[event][time] += 1

# Get all event keys
names = sorted(d.keys())
# Initialize overall total value
otot = 0
# For every event name
for k in names:
    # Reinitalize tot
    tot = 0
    # For every time for event 
    for i in range(0,time+1):
        tmp = d[k][i]
        d[k][i] += tot
        tot += tmp
    otot += tot

vecs = np.array([d[k] for k in names])

# Plot it
fig = plt.figure()
ax = fig.add_subplot(111)

params = {'edgecolor':'none', 'width':1}
colors = cycle(['#848484', '#FF0000', '#04B404', '#8904B1', '#FFBF00', '#FF0080', '#0404B4', '#E2A9F3', '#A9D0F5', '#FFFF00', '#58ACFA'])

ax.bar(range(100000), vecs[0],  facecolor=colors.next(), label=names[0], **params)
for i in range(1, len(vecs)):
    ax.bar(range(100000), vecs[i], bottom=vecs[:i,:].sum(axis=0), 
           facecolor=colors.next(), label=names[i], **params)

ax.set_xticks(range(time+1))
ax.set_yticks(range(otot+1))
ax.legend(loc='upper left')

plt.show()

enter image description here

3 个答案:

答案 0 :(得分:2)

根据您发布的输入数据,您发布的地图是错误的。例如,'A_EVENT'未显示在TIME 6,因此您的地图中x=6处的灰色框不应该出现在那里。

无论如何,我不得不重写代码。正如@tcaswell所说,读起来很痛苦。这是一个更简单的版本。

import numpy as np
from itertools import cycle
from collections import defaultdict
from matplotlib import pyplot as plt

# Get values from 'test.txt'
d = defaultdict(lambda : [0]*10)
with open('test.txt', 'r') as infile:
    for line in infile:
        tokens = line.rstrip().split(" ")
        time = int(tokens[1])
        event = tokens[2]
        d[event][time] += 1

names = sorted(d.keys())
vecs = np.array([d[k] for k in names])

# Plot it
fig = plt.figure()
ax = fig.add_subplot(111)

params = {'edgecolor':'none', 'width':1}
colors = cycle(['r', 'g', 'b', 'm', 'c', 'Orange', 'Pink'])

ax.bar(range(10), vecs[0],  facecolor=colors.next(), label=names[0], **params)
for i in range(1, len(vecs)):
    ax.bar(range(10), vecs[i], bottom=vecs[:i,:].sum(axis=0), 
           facecolor=colors.next(), label=names[i], **params)

ax.set_xticks(range(10))
ax.set_yticks(range(10))
ax.legend(loc='upper left')

plt.show()

产生字典d

[('A_EVENT', [0, 0, 0, 0, 0, 1, 0, 2, 2, 0]), 
 ('B_EVENT', [0, 0, 0, 0, 0, 0, 1, 0, 0, 0]), 
 ('D_EVENT', [0, 0, 0, 0, 0, 0, 0, 1, 0, 0]), 
 ('J_EVENT', [0, 0, 0, 0, 0, 0, 0, 0, 1, 0]), 
 ('C_EVENT', [0, 0, 0, 0, 0, 0, 1, 0, 0, 0]), 
 ('E_EVENT', [0, 0, 0, 0, 0, 0, 0, 0, 1, 0])]

和向量vecs

[[0 0 0 0 0 1 0 2 2 0]
 [0 0 0 0 0 0 1 0 0 0]
 [0 0 0 0 0 0 1 0 0 0]
 [0 0 0 0 0 0 0 1 0 0]
 [0 0 0 0 0 0 0 0 1 0]
 [0 0 0 0 0 0 0 0 1 0]]

和图 enter image description here

答案 1 :(得分:1)

我明白了,我并没有完全掌握这样一个事实:你正试图制造~100条 非常耗费内存的条形码。我会建议这样的事情:

import numpy as np
from itertools import izip, cycle
import matplotlib.pyplot as plt
from collections import defaultdict

N = 100

fake_data = {}
for j in range(97, 104):
    lab = chr(j)
    fake_data[lab] = np.cumsum(np.random.rand(N) > np.random.rand(1))

colors = cycle(['r', 'g', 'b', 'm', 'c', 'Orange', 'Pink'])

# fig, ax = plt.subplots(1, 1, tight_layout=True) # if your mpl is newenough
fig, ax = plt.subplots(1, 1) # other wise
ax.set_xlabel('time')
ax.set_ylabel('counts')
cum_array = np.zeros(N*2 - 1) # to keep track of the bottoms
x = np.vstack([arange(N), arange(N)]).T.ravel()[1:] # [0, 1, 1, 2, 2, ..., N-2, N-2, N-1, N-1]
hands = []
labs = []
for k, c in izip(sorted(fake_data.keys()), colors):
    d = fake_data[k]
    dd = np.vstack([d, d]).T.ravel()[:-1]  # double up the data to match the x values [x0, x0, x1, x1, ... xN-2, xN-1]
    ax.fill_between(x, dd + cum_array, cum_array,  facecolor=c, label=k, edgecolor='none') # fill the region
    cum_array += dd                       # update the base line
    # make a legend entry
    hands.append(matplotlib.patches.Rectangle([0, 0], 1, 1, color=c)) # dummy artist
    labs.append(k)                        # label

ax.set_xlim([0, N - 1]) # set the limits 
ax.legend(hands, labs, loc=2)             #add legend
plt.show()                                #make sure it shows

对于N = 100:

N=100 demo

对于N = 100000:

N=100000

这使用〜几百兆。

作为旁注,数据解析可以进一步简化为:

import numpy as np
from itertools import izip
import matplotlib.pyplot as plt
from collections import defaultdict

# this requires you to know a head of time how many times you have
len = 10
d = defaultdict(lambda : np.zeros(len, dtype=np.bool)) # save space!
with open('test.txt', 'r') as infile:
    infile.next() # skip the header line
    for line in infile:
        tokens = line.rstrip().split(" ")
        time = int(tokens[0]) # get the time which is the first token
        for e in tokens[1:]:  # loop over the rest
            if len(e) == 0:
                pass
            d[e][time] = True

for k in d:
    d[k] = np.cumsum(d[k])

没有严格测试,但我认为它应该有用。

答案 2 :(得分:0)

如果没有正确关闭绘图,

matplotlib会导致内存泄漏。 this gist解释了替代方案。没有你的代码,很难说你的问题是什么。