Esempio n. 1
0
File: Maze.py Progetto: r-b-g-b/Lab
def corr_vs_incorr_targets(data):
	
	'''
	shows the locations of targets that were correctly found versus targets that were missed
	'''
	
	target_corr = np.array([dat for dat in data if dat['target'] == 1])
	target_incorr = np.array([dat for dat in data if dat['target'] == 0])
	target_loc = np.array([dat for dat in data])
	
	fig = plt.figure();
	
	# correct trial target locations
	ax1 = fig.add_subplot(131);
	plot_heatmap(target_corr, epoch = 'target', ax = ax1, gridsize = 20)

	# incorrect trial target locations
	ax2 = fig.add_subplot(132);
	plot_heatmap(target_incorr, epoch = 'target', ax = ax2, gridsize = 20)
	
	# all trial target locations
	ax3 = fig.add_subplot(133);
	plot_heatmap(target_loc, epoch = 'target', ax = ax3, gridsize = 20)
	misc.sameyaxis([ax1, ax2, ax3])
	misc.samexaxis([ax1, ax2, ax3])
Esempio n. 2
0
def plot_trials(df):

	plt.close('all');
	fig1 = plt.figure();
	fig2 = plt.figure();
	ax1 = []; ax2 = []
	hemis = ['r', 'l']
	for j, hemi in enumerate(hemis[0]):
		df_ = df[df.hemi==hemi]
		ntrials = df_.lfp.size
		nsamp = df_.lfp[df_.index[0]].size
		for k, i in enumerate(range(ntrials)[::40]):
			ax1.append(fig1.add_subplot(5, 5, k+1))
			ix = df_.index[i]
			ax1[-1].hist(df_.lfp[ix], bins = 100)
			ax1[-1].set_xticklabels('')
			ax1[-1].set_yticklabels('')
			ax1[-1].set_title(ix)

			ax2.append(fig2.add_subplot(5, 5, k+1))
			ix = df_.index[i]
			ax2[-1].plot(df_.lfp[ix])
			ax2[-1].set_xticklabels('')
			ax2[-1].set_yticklabels('')

	misc.samexaxis(ax1)
	misc.sameyaxis(ax2)
Esempio n. 3
0
def make_noise_rr_sheets():

	fpaths = glob.glob(os.path.join(studydir, 'Sessions', 'Pre', 'both',  '*.h5'))

	for fpath in fpaths:

		absol, relat = os.path.split(fpath)
		fname, _ = os.path.splitext(relat)

		f = pd.HDFStore(fpath, 'r')
		df = f['df']
		f.close()
		df = df[df.rr>0] # subset only non-silent trials
		df = df[df.good] # use only good trials
		urrs = np.unique(df.rr)
		nrrs = urrs.size

		gp = df.groupby(('rr', 'hemi'))

		nsamp = df.lfp.values[0].size
		Fs = nsamp / trial_duration

		lfp_mean = gp.lfp.apply(np.mean) 
		lfp_err = gp.lfp.apply(np.std)

		fft_mean = gp.fft.apply(np.mean)
		fft_err = gp.fft.apply(np.std)

		t = np.linspace(0, trial_duration, nsamp)
		f = np.linspace(0, Fs/2., df.fft.values[0].size)

		fig = plt.figure(figsize = (14, 8.8))
		ax_lfp = []; ax_fft = []
		for i, ((k, lfp_mean_), (k, lfp_err_), (k, fft_mean_), (k, fft_err_)) in enumerate(zip(lfp_mean.iterkv(), lfp_err.iterkv(),\
			fft_mean.iterkv(), fft_err.iterkv())):

			rr, hemi = k
			if hemi=='l':
				j=1
			else: j=2

			ax_lfp.append(fig.add_subplot(nrrs, 4, ((i/2)*4)+j))
			misc.errorfill(t, lfp_mean_, lfp_err_, ax = ax_lfp[-1])
			ax_lfp[-1].set_title(k)

			ax_fft.append(fig.add_subplot(nrrs, 4, ((i/2)*4)+2+j))
			misc.errorfill(f, np.abs(fft_mean_), np.abs(fft_err_), ax = ax_fft[-1])


		misc.sameyaxis(ax_lfp); misc.sameyaxis(ax_fft)
		misc.samexaxis(ax_lfp, [0, stim_duration]); misc.samexaxis(ax_fft, [0, 100.])
		[a.set_yticklabels('') for a in ax_lfp[1::2]]
		fig.savefig(os.path.join(studydir, 'Sheets', '%s_noise.png'%fname))
		plt.close(fig)
Esempio n. 4
0
def plot_drugs_by_dist(df):
	fig = plt.figure();
	ax = []
	udist = np.unique(df.PdDistrict)
	for i, dist in enumerate(udist):
		df_ = df[df.PdDistrict==dist]
		ax.append(fig.add_subplot(2, 5, i+1))
		df_.Drug.value_counts()[:5].plot(kind = 'bar', ax = ax[-1])
		ax[-1].set_title(dist)

	misc.sameyaxis(ax)
Esempio n. 5
0
def make_silent_sheets(epoch = 'Pre'):

	sesspaths = glob.glob(os.path.join(studydir, 'Sessions', epoch, '*'))
	for sesspath in sesspaths:
		fpaths = glob.glob(os.path.join(sesspath, 'fileconversion', '*.h5'))

		for fpath in fpaths:

			absol, relat = os.path.split(fpath)
			fname, _ = os.path.splitext(relat)
			
			f = pd.HDFStore(fpath, 'r')
			df = f['df']
			f.close()

			df = df[df.rr==-1]
			df = df[df.good]
			gp = df.groupby('hemi')

			nsamp = df.lfp.values[0].size
			Fs = nsamp / trial_duration

			lfp_mean = gp.lfp.apply(np.mean)
			lfp_err = gp.lfp.apply(np.std)

			fft_mean = gp.fft.apply(np.mean)
			fft_err = gp.fft.apply(np.std)

			t = np.linspace(0, trial_duration, nsamp)
			f = np.linspace(0, Fs/2., df.fft.values[0].size)

			fig = plt.figure()
			ax_lfp = []; ax_fft = []
			for i, ((k, lfp_mean_), (k, lfp_err_), (k, fft_mean_), (k, fft_err_)) in enumerate(zip(lfp_mean.iterkv(), lfp_err.iterkv(),\
				fft_mean.iterkv(), fft_err.iterkv())):
				# lfp_err_ = lfp_err[k]
				ax_lfp.append(fig.add_subplot(2, 2, i+1));
				misc.errorfill(t, lfp_mean_, lfp_err_, ax = ax_lfp[-1])
				ax_lfp[-1].set_title(k)

				ax_fft.append(fig.add_subplot(2, 2, i+3));
				misc.errorfill(f, np.log(fft_mean_), np.log(fft_err_), ax = ax_fft[-1])

			misc.sameyaxis(ax_lfp); misc.sameyaxis(ax_fft)
			misc.samexaxis(ax_lfp, [0, stim_duration]); misc.samexaxis(ax_fft, [f.min(), f.max()])

			figpath = os.path.join(studydir, 'Sheets', '%s_silent.png' % fname)
			fig.savefig(figpath)
			plt.close(fig)
Esempio n. 6
0
def make_noise_singleburst_sheets():

	fpaths = glob.glob(os.path.join(studydir, 'Sessions', 'Pre', 'both',  '*.h5'))

	fig = plt.figure(figsize = (14, 8.8))
	ax_lfp = []

	for j, fpath in enumerate(fpaths):

		absol, relat = os.path.split(fpath)
		fname, _ = os.path.splitext(relat)

		f = pd.HDFStore(fpath, 'r')
		df = f['df']
		f.close()
		df = df[np.logical_and(df.rr>0, df.rr<16, df.good)] # subset only non-silent trials

		gp = df.groupby('hemi')

		nsamp = df.lfp.values[0].size
		Fs = nsamp / trial_duration

		lfp_mean = gp.lfp.apply(np.mean) 
		lfp_err = gp.lfp.apply(np.std)

		fft_mean = gp.fft.apply(np.mean)
		fft_err = gp.fft.apply(np.std)

		t = np.linspace(0, trial_duration, nsamp)
		f = np.linspace(0, Fs/2., df.fft.values[0].size)

		for i, ((k, lfp_mean_), (k, lfp_err_), (k, fft_mean_), (k, fft_err_)) in enumerate(zip(lfp_mean.iterkv(), lfp_err.iterkv(),\
			fft_mean.iterkv(), fft_err.iterkv())):

			ax_lfp.append(fig.add_subplot(len(fpaths), 2, (j*2)+i+1))
			misc.errorfill(t, lfp_mean_, lfp_err_, ax = ax_lfp[-1])
			if i==0:
				ax_lfp[-1].set_title('%s / %s' % (fname, k))
			else:
				ax_lfp[-1].set_title(k)

	misc.sameyaxis(ax_lfp, [-0.0004, 0.0004])
	misc.samexaxis(ax_lfp, [0.05, 0.175])
	[a.set_yticklabels('') for a in ax_lfp[1::2]]
	fig.savefig(os.path.join(studydir, 'Sheets', 'noise_singleburst.png'))
	plt.close(fig)
Esempio n. 7
0
def make_contactsheets():

	subjIDs = ['Red102', 'Green102', 'Blue102', 'Red101']
	epochs = ['Pre', 'Post']

	for subjID in subjIDs:
		for epoch in epochs:
			sesspaths = glob.glob(os.path.join(studydir, 'Sessions', epoch, '*'))
			for sesspath in sesspaths:
				# fpaths = 
				for fpath in fpaths:

					absol, relat = os.path.split(fpath)
					fname, _ = os.path.splitext(relat)
					print fname

					fin = pd.HDFStore(fpath, 'r')
					df = fin['df']
					fin.close()

					ntrials = 100
					duration = 0.333
					nattens = np.unique(df.atten).size

					gp = df.groupby(('atten', 'hemi', 'relhemi'))
					means = gp.lfp.apply(np.mean)
					errs = gp.lfp.apply(np.std) / float(np.sqrt(ntrials))

					t = np.linspace(0, duration, df.lfp[0].size)

					fig = plt.figure()
					ax = []
					for i, ((k, m), (k, er)) in enumerate(zip(means.iterkv(), errs.iterkv())):

						ax.append(fig.add_subplot(nattens, 4, i+1))
						misc.errorfill(t[:-1], m[:-1], er[:-1], ax = ax[-1])
						ax[-1].set_title(k)
						ax[-1].set_xlim([0, duration])

					misc.sameyaxis(ax)
					[a.set_xticklabels('') for a in ax[:-1]]
					[a.set_yticklabels('') for a in ax[1:]]
					fig.tight_layout()
					fig.savefig(os.path.join(studydir, 'Sheets', '%s_sheet.png' % fname))
					plt.close(fig)
Esempio n. 8
0
File: ABR.py Progetto: r-b-g-b/Lab
	def plot_peak_gen_vs_exp(self, x, measure = 'ampl'):
		'''
		'''
		gens = ['wt', 'ko']
		exps = ['nai', 'exp']
		freqs = [8000., 16000.]

		plt_opts = {'wt' : {'color' : 'b', 'x_offset' : 0}, 'ko' : {'color' : 'r', 'x_offset' : 1}}

		fig = plt.figure(figsize = (14, 8))
		ax = []
		for i in range(10):
			ax.append(fig.add_subplot(2, 5, i+1))
			ax[-1].set_title('Peak %i' % ((i%5)+1))

		a = np.arange(5)
		for f, freq in enumerate(freqs):
			x2 = x[x['freq']==freq]
			attens = np.unique(x2['atten'])
			for atten in attens[:1]:
				x3 = x2[x2['atten']==atten]
			
				for gen in gens:
					ampl = np.empty((len(exps), 5))
					ampl_err = np.empty((len(exps), 5))
					for j, exp in enumerate(exps):
						x4 = x3[np.vstack((x3['gen']==gen, x3['exp']==exp)).all(0)]
						ampl[j, :] = (x4[measure]*10).mean(0)
						ampl_err[j, :] = (x4[measure]*10).std(0) / np.sqrt(x4.size)
					for i in range(ampl.shape[1]):
						ampl_ = ampl[:, i]
						ampl_err_ = ampl_err[:, i]
						ax[(f*5)+i].errorbar(np.arange(2), ampl_, yerr = ampl_err_, color = plt_opts[gen]['color'])

		misc.sameyaxis(ax)
		misc.samexaxis(ax, [-1, 2])
		for i in range(10):
			ax[i].set_xticks([0, 1])
			ax[i].set_xticklabels(exps)
		for i in range(1, 10):
			ax[i].set_yticklabels([])

		plt.show()	
Esempio n. 9
0
def dep_by_indep_for_2_groups(dep_key, indep_key, group1_key, group2_key, data, v = False):

	'''
	takes 2-D data and splits it up by two groups
	it plots group1 groups on separate axes and group2 groups as different colors on the same axis
	'''
	
	group1 = np.unique(data[group1_key]); ngroup1 = len(group1)
	group2 = np.unique(data[group2_key]); ngroup2 = len(group2)

	leg = []
	line = []
	for i in range(ngroup1):
		leg.append([])
		line.append([])
	colors = 'bgmyrc'

	fig = plt.figure()
	fig.tight_layout()
	for i, (g1, g2) in enumerate(itertools.product(range(ngroup1), range(ngroup2))):
		if v:
			print group1[g1], group2[g2]
		data_ = data[np.vstack((data[group1_key]==group1[g1], data[group2_key]==group2[g2])).all(0)]
		ax = fig.add_subplot(1, ngroup1, g1+1)
		ax.set_title(group1[g1])
		if data_.size > 0:
			line_, ax_ = bar_by_indep_2d(dep_key, indep_key, data_, ax = ax, color = colors[g2])
			line[g1].append(line_)
			leg[g1].append(group2[g2])

	for g1 in range(ngroup1):
		ax = fig.add_subplot(1, ngroup1, g1+1)
		ax.legend(line[g1], leg[g1])
	
	misc.samexaxis(fig.get_axes())
	misc.sameyaxis(fig.get_axes())
	
	plt.show()
	
	return fig
Esempio n. 10
0
File: RR.py Progetto: r-b-g-b/Lab
def circ_psth_all(rast, stimparams, freq, npips, onset = 0.05, bins = 20, color = 'b', remove_first = False, axs = None):
	'''
	Input:
	Output:
		r :  the mean vector length for each repetition rate
		V : the summed vector length for each repetition rate
		theta : the mean vector angle for each repetition rate
	'''
	urrs = np.unique(stimparams[:, 1])
	nrrs = urrs.size
	ix = RF.get_trials(stimparams, (freq, np.nan))
	rast_ = rast[ix, :]
	stimparams_ = stimparams[ix, :]
	r = []; V = []; theta = []
	
	for i in xrange(nrrs):
		ix = RF.get_trials(stimparams_, (np.nan, urrs[i]))
		r_, V_, theta_ = circ_psth(rast_[ix, :], urrs[i], npips[i], onset = onset, bins = bins, color = color, remove_first = remove_first, ax = axs[i])
		r.append(r_); V.append(V_); theta.append(theta_)
	
	misc.sameyaxis(axs)

	return np.array(r), np.array(V), np.array(theta)
Esempio n. 11
0
File: ABR.py Progetto: r-b-g-b/Lab
	def plot_threshold_gen_vs_exp(self, x):
		'''
		'''
		gens = ['wt', 'ko']
		exps = ['nai', 'exp']
		freqs = np.unique(x['freq'])[:-1]

		plt_opts = {'wt' : {'color' : 'b', 'x_offset' : 0}, 'ko' : {'color' : 'r', 'x_offset' : 1}}

		fig = plt.figure(figsize = (14, 4))
		ax = []
		for i, freq in enumerate(freqs):
			ax.append(fig.add_subplot(1, 4, i+1))
			ax[-1].set_title('%u kHz' % (freq/1000))
			for j, gen in enumerate(gens):
				Y = np.empty(2)
				Y_err = np.empty(2)
				for k, exp in enumerate(exps):
					x_ = x[np.vstack((x['gen']==gen, x['exp']==exp, x['freq']==freq)).all(0)]
					y = x_['thresh_calib'].mean()
					y_err = x_['thresh_calib'].std() / np.sqrt(x_.size)
					
					Y[k] = y
					Y_err[k] = y_err
			
				ax[-1].errorbar([1, 2], Y, yerr = Y_err, color = plt_opts[gen]['color'])
		
		misc.sameyaxis(ax)
		misc.samexaxis(ax, [0, 3])
		for i in range(4):
			ax[i].set_xticks([1, 2])
			ax[i].set_xticklabels(exps)
		for i in range(1, 4):
			ax[i].set_yticklabels([])

		plt.show()	
Esempio n. 12
0
		rr_psth_stim, usp = Spikes.calc_psth_by_stim(rr_rast, rr_stimparams, bins = rr_bins)
		cf_psth = rr_psth_stim[freq_ix_played, :, :]
		noise_psth = rr_psth_stim[0, :, :]
		
		fig = plt.figure()
		nrrs = cf_psth.shape[0]
		
		ax = []
		for i in range(nrrs):
			ax.append(fig.add_subplot(nrrs, 1, i+1))
			ax[-1].plot(rr_bins[:-1], cf_psth[i, :])
			RR.plot_tone_pips(urrs[i], 6, 0.05, 0.025, ax = ax[-1], color = 'r')
			if i>0:
				ax[-1].set_xticklabels('')
			
		misc.sameyaxis(ax)
	
	if len(voc_path) > 0:
		
		voc_file = h5py.File(voc_path, 'r')
		voc_rast = voc_file['rast'].value
		voc_stimparams = voc_file['stimID'].value
		voc_file.close()
		
		# voc_rast = voc_rast[1:, :]
		# voc_stimparams = voc_stimparams[:-1, :]
		voc_stimparams = voc_stimparams[:, 0]
		voc_stimparams = np.hstack((voc_stimparams[..., np.newaxis], np.zeros((voc_stimparams.size, 1))))
		uvocs = np.unique(voc_stimparams[:, 0])
		nvocs = uvocs.size
		
Esempio n. 13
0
def rr_make_contactsheets():
	'''
	loop through all the sessions and plot the rrtfs
	'''

	fig = plt.figure(figsize = (30, 18));
	txt_suptitle = fig.suptitle('')
	ax_cfrrtf = fig.add_axes((0.76, 0.76, 0.24, 0.23));
	ax_cfvs = ax_cfrrtf.twinx();
	ax_cfcircpsthall = fig.add_axes((0.62, (11/14.)-0.02, 0.1, (1/7.)+0.04), polar = True)
	ax_cfcircpsthall.set_xticklabels(''); ax_cfcircpsthall.set_yticklabels('');
	ax_rf = fig.add_axes((0.67, 0.51, 0.33, 0.23));
	ax_rfrast = fig.add_axes((0.67, 0.25, 0.33, 0.24));
	ax_rfrast.set_xticklabels('');
	ax_rfpsth = fig.add_axes((0.67, 0.01, 0.33, 0.24));

	ax_cfrr = [fig.add_axes((0.03, 1-((i+1)/7.), 0.35, 1/7.)) for i in np.arange(nrrs)]
	ax_cfalignedpsth = [fig.add_axes((0.38, 1-((i+1)/7.), 0.17, 1/7.)) for i in np.arange(nrrs)]
	ax_cfcircpsth = [fig.add_axes((0.53, 1-((i+1)/7.), 0.1, 1/7.), polar = True) for i in np.arange(nrrs)]
	# ax_noiserr = [fig.add_subplot(nrrs, 3, i) for i in np.arange(1, 3*nrrs, 3)]

	for sessionpath in sessionpaths:

		session = os.path.split(sessionpath)[1]
		unitinfos = fileconversion.get_session_unitinfo(sessionpath, onlycomplete = ('RF', 'RR', 'VOC'))
			
		for unitkey in unitinfos.keys():
			
			txt_suptitle.set_text('%s %s' % (session, unitkey))

			unitinfo = unitinfos[unitkey]

			rf_ix = unitinfo['stimtype'].index('RF')
			
			f_rf = h5py.File(unitinfo['fpath'][rf_ix], 'r')
			rf_rast = f_rf['rast'].value
			rf_stimparams = f_rf['stimID'].value
			cf_ix = f_rf['cf'].value
			f_rf.close()
			
			cf = ix2freq[20:][int(cf_ix)]

			''' calculate and plot RF, psth, and sorted raster'''
			rf = RF.calc_rf(rf_rast, rf_stimparams)
			rf_psth = Spikes.calc_psth(rf_rast)
			RF.plot_rf(rf, cf = cf_ix, axes_on = False, ax = ax_rf) # plot RF
			ax_rf.axvline(cf_ix, color = 'r', lw = 1.5)
			Spikes.plot_sorted_raster(rf_rast, rf_stimparams, ax = ax_rfrast) # plot raster
			ax_rfpsth.plot(t_rf, Spikes.exp_smoo(rf_psth, tau = 0.005)) # plot PSTH

			''' calcualte and plot RRTFs for CF and noise stimuli '''
			rr_ix = unitinfo['stimtype'].index('RR')
			
			f_rr = h5py.File(unitinfo['fpath'][rr_ix], 'r')
			rr_rast = f_rr['rast'].value
			rr_stimparams = f_rr['stimID'].value
			f_rr.close()

			# find the played CF
			rr_ufreqs = np.unique(rr_stimparams[:, 0])
			urrs = np.unique(rr_stimparams[:, 1])
			npips = (urrs*4).astype(int)
			rr_freq, rr_ufreq_ix, _ = misc.closest(rr_ufreqs, cf, log = True)

			ax_rf.axvline(RF.calc_freq2ix(rr_freq), color = 'g', lw = 1.5)
			# calculate the PSTHs for each repetition rate
			tmp = Spikes.calc_psth_by_stim(rr_rast, rr_stimparams)
			rr_cfpth = tmp[0][rr_ufreq_ix, :, :]
			# rrtf_noisepsth = tmp[0][0, :, :]

			# plot the aligned psths
			RR.aligned_psth_separate_all(rr_rast, rr_stimparams, rr_freq, npips, axs = ax_cfalignedpsth)
			[a.set_yticklabels('') for a in ax_cfalignedpsth]
			[a.set_xticklabels('') for a in ax_cfalignedpsth[:-1]]

			# plot circular psths
			r, V, theta = RR.circ_psth_all(rr_rast, rr_stimparams, rr_freq, npips, axs = ax_cfcircpsth)
			[a.set_yticklabels('') for a in ax_cfcircpsth]
			[a.set_xticklabels('') for a in ax_cfcircpsth]

			# plot all circular summed vector strengths
			ax_cfcircpsthall.plot(theta, V, '.-')
			[ax_cfcircpsthall.plot([0, th], [0, v], color = 'b', alpha = 1-(i/10.)) for i, (th, v) in enumerate(zip(theta, V))]


			# plot RRTF
			rrtf = RR.calc_rrtf_all(rr_rast, rr_stimparams, rr_freq, urrs, npips)
			ax_cfrrtf.plot(rrtf, '.-', ms = 10)
			ax_cfvs.plot(V*np.cos(theta), 'g.-', ms = 10)
			for tick in ax_cfvs.yaxis.get_major_ticks():
				tick.set_pad(-5)
				tick.label2.set_horizontalalignment('right')

			# plot repetition rate PSTHs
			for i in xrange(nrrs):
				# RR.plot_rrtf(t_rrtf, rrtf_noisepsth[i, :], urrs[i], int(4*urrs[i]), onset = 0.05, duration = 0.025, ax = ax_noiserr[i])
				RR.plot_rrtf(t_rrtf, rr_cfpth[i, :], urrs[i], int(4*urrs[i]), onset = 0.05, duration = 0.025, ax = ax_cfrr[i])

			# ax_noiserr[0].set_title('Noise RRTFs')
			ax_cfrr[0].set_title('CF RRTFs (%.0f kHz)' % (cf/1000))
			# [a.set_xlim(0, 4.5) for a in ax_noiserr]
			[a.set_xlim(0, 4.5) for a in ax_cfrr]
			misc.sameyaxis(ax_cfrr+ax_cfalignedpsth)

			figsavepath = os.path.join(studydir, 'Sheets', 'RRTFs', '%s_%s_RRTF.png' % (session, unitkey))
			print figsavepath
			fig.savefig(figsavepath)
			[a.cla() for a in fig.get_axes()] # clear all axes
Esempio n. 14
0
def bar_by_indep_by_group_3d(dep_key, indep_key, data, visible = True, ax = None, group_keys = 'gen', bins = None, iscategorical = True, show_all = False, use_bar = False, fig = None, **kwargs):
	'''
	plots a comparison over many groups
	attemps to make multiple line plots containing 2 lines per plot for ease-of-viewing
	gen exp sess
	'''
	
	ngroups = len(group_keys) # number of groups
	# bin data for each group
	if bins is None:
		bins = [None] * ngroups
		
	groups = [] # actual entries for each group
	groups_bins = []
	ugroups = []
	nbins_per_group = []
	for bin, group_key in zip(bins, group_keys):

		groups.append(data[group_key])
		if bin is None:
			groups_bins.append(groups[-1]) # binned labels for each data point
			ugroups.append(np.unique(groups[-1])) # all unique binned labels
		else:
			groups_bins.append(misc.bin(groups[-1], bin))
			ugroups.append(bin[:-1])

		nbins_per_group.append(ugroups[-1].size) # how many unique labels in each group

	''' ix is for all possible group combinations, a boolean vector indicating membership of each data point in the data set
		l is passed to itertools.product to iterate through all combinations of group values
	'''
	ix, l = build_index(groups_bins, ugroups, nbins_per_group)

	if fig is None:
		fig = plt.figure(figsize = (11, 14));
		
	naxs1 = nbins_per_group[0] # exp / row
	naxs2 = nbins_per_group[1] # cf / column		

	cm = plt.cm.gist_ncar
	clrs = [cm(i) for i in np.linspace(0, 0.9, nbins_per_group[-1])]

	nlines = np.zeros((nbins_per_group[0], nbins_per_group[1]))
	for i in itertools.product(*l):
		print i
		axisnum = i[0]*naxs2 + i[1] + 1
		ax = fig.add_subplot(naxs1, naxs2, axisnum)
		if ix[i].sum()>0:
			nlines[i[0], i[1]] += 1
			bar_by_indep_2d(dep_key, indep_key, data[ix[i]], ax = ax, color = clrs[i[-1]])

			if i[1] == 0:
				ax.set_ylabel('%s\n%s' % (str(ugroups[0][i[0]]), dep_key), multialignment = 'center')
			if i[0] == 0:
				ax.set_title('%s = %s' % (str(group_keys[1]), str(ugroups[1][i[1]])))
			else:
				ax.set_title('')
	
	axs = fig.get_axes()
	misc.sameyaxis(axs)
	misc.samexaxis(axs)
	done = False
	i = 0
	while not done:
		i += 1
		if nlines[i % nbins_per_group[0], int(np.floor(i/nbins_per_group[0]))] == nbins_per_group[2]:
			axs[i].legend(ugroups[2])
			done = True
	
	plt.show()
	return fig