Skip to content

Instantly share code, notes, and snippets.

@smestern
Last active September 2, 2020 17:47
Show Gist options
  • Save smestern/e0c08c0106efd2b11482e25283bce060 to your computer and use it in GitHub Desktop.
Save smestern/e0c08c0106efd2b11482e25283bce060 to your computer and use it in GitHub Desktop.
Create an 'unknown pleasures'-style artwork out of a real neuron's action potentials (Or really any other signal recorded in abf format) [Utilizes matplotlib's unchained.py and pyabf]
"""
========================
MATPLOTLIB **UNCHAINED**
========================
Orignal Author: Nicolas P. Rougier
This is a modified version.
Modified to take an abf file to visualize the action potentials present.
Preview: https://i.imgur.com/85hgMJm.mp4
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import pyabf
import tkinter as tk
from tkinter import filedialog
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
root = tk.Tk()
root.withdraw()
sweepdsply=7
file_path = filedialog.askopenfilename()
abf = pyabf.ABF(file_path)
# Fixing random state for reproducibility
abf.setSweep(0)
# Create new Figure with black background
fig = plt.figure(figsize=(8, 8), facecolor='black')
# Add a subplot with no frame
ax = plt.subplot(111, frameon=False)
abfdata = np.hstack(scaler.fit_transform(abf.sweepY.reshape(-1,1)))
# Generate data
i1 = abf.dataPointsPerMs * 0
i2 = abf.dataPointsPerMs * 500
ifull = i2 - i1
iar = np.array((i1, i2))
data = np.array(abfdata)
for i in range(0,35):
i1 = int(np.random.uniform(0, 6000, 1)[0])
i2 = i1+ifull
iar = np.vstack((iar, (i1, i2)))
data = np.vstack((data, abfdata))
X = np.linspace(-1, 1, ifull)
G = 9.5 * np.exp(-4 * X ** 2)
spacefac = 1.5
speedfac = 100
# Generate line plots
lines = []
for i in range(len(data)):
i1 = iar[i, 0]
i2 = iar[i, 1]
# Small reduction of the X extents to get a cheap perspective effect
xscale = 1 - i / 100.
# Same for linewidth (thicker strokes on bottom)
lw = 1.5 - i / 70.0
line, = ax.plot(xscale * X, (i * spacefac) + G * data[i, i1:i2], color="w", lw=lw)
lines.append(line)
# Set y limit (or first line is cropped because of thickness)
ax.set_ylim(-1, 70)
# No ticks
ax.set_xticks([])
ax.set_yticks([])
# 2 part titles to get different font weights
ax.text(0.5, 1.0, "UNKNOWN ", transform=ax.transAxes,
ha="right", va="bottom", color="w",
family="sans-serif", fontweight="light", fontsize=16)
ax.text(0.5, 1.0, "MEASURES", transform=ax.transAxes,
ha="left", va="bottom", color="w",
family="sans-serif", fontweight="bold", fontsize=16)
def update(frame, *args):
global i1, i2
global abf
global abfdata
global ifull
# Update data
for i in range(len(data)):
i1 = iar[i, 0]
i2 = iar[i, 1]
if i2 > (data[i].shape[0] - (speedfac+1)):
data[i] = np.append(data[i, i1:], abfdata[:i1])
i1 = 0
i2 = i1 + ifull
i1 +=speedfac
i2 +=speedfac
lines[i].set_ydata((i * spacefac) + G * data[i, i1:i2])
iar[i,0] = i1
iar[i,1] = i2
# Return modified artists
return lines
# Construct the animation, using the update function as the animation director.
anim = animation.FuncAnimation(fig, update, interval=10)
plt.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment