I think that putting the data into a graph-based representation is a good approach for the problem as described, but perhaps you have a use-case where this is too heavy-weight. In the former, @xg.pltpy has made a suggestion already.
Here is one way to do it solely in matplotlib, using the powerful annotate
functionality.
import matplotlib.pyplot as plt
# define drawing of the words and links separately.
def plot_words(wordlist, col, ax):
bbox_props = dict(boxstyle="round4,pad=0.3", fc="none", ec="b", lw=2)
for i, word in enumerate(wordlist):
ax.text(col, i, word, ha="center", va="center",
size=12, bbox=bbox_props)
def plot_links(list1, list2, cols, ax):
connectionstyle = "arc3,rad=0"
for i, word in enumerate(list1):
try: # do we need an edge?
j = list2.index(word)
except ValueError:
continue # move on to the next word
# define coordinates (relabelling here for clarity only)
y1, y2 = i, j
x1, x2 = cols
# draw a line from word in 1st list to word in 2nd list
ax.annotate("", xy=(x2, y2), xycoords='data',
xytext=(x1, y1), textcoords='data',
arrowprops=dict(
arrowstyle="->", color="k", lw=2,
shrinkA=25, shrinkB=25, patchA=None, patchB=None,
connectionstyle=connectionstyle,))
# define several lists
list1 = ['Cat', 'Dog', 'Elephant', 'Giraffe', 'Monkey']
list2 = ['Cat', 'Dog', 'Eagle', 'Elephant', 'Monkey']
list3 = ['Cat', 'Mouse', 'Horse', 'Elephant', 'Monkey']
# now plot them all -- words first then links between them
plt.figure(1); plt.clf()
fig, ax = plt.subplots(num=1)
plot_words(list1, col=1, ax=ax)
plot_words(list2, col=2, ax=ax)
plot_words(list3, col=0, ax=ax)
plot_links(list1, list2, ax=ax, cols=[1,2])
plot_links(list1, list3, ax=ax, cols=[1,0])
ax.set_xlim(-0.5, 2.5)
ax.set_ylim(-0.5, len(list1)+0.5)

There are LOTs of options for the arrow type, see demo.
It would be cleaner to supply the patchA
and patchB
arguments in arrowprops, since annotate then automatically clips the arrow length to avoid the patches (here, the words). I leave that as an exercise for the reader ;)