Python Dictionary Manipulation
string.punctuation == string.punctuation.lower().
You don't need string_clean to be a function as you only use it once.
Don't use _ as a variable, and definitely don't in a loop, as most use it as a 'garbage' variable.
You can use f[:-1] to get the same as u = f[:];u.pop()
Your algorithm is ok, but can be a bit odd to read.


To improve your code I'd add collections.defaultdict.
This will allow you to remove the innermost if/else.
This is as if the value isn't in the dictionary it'll default it to something for you.

&gt;&gt;&gt; from collections import defaultdict
&gt;&gt;&gt; next_word_keeper = defaultdict(int)
&gt;&gt;&gt; next_word_keeper['test'] += 1
&gt;&gt;&gt; next_word_keeper
defaultdict(&lt;type 'int'&gt;, {'test': 1})
&gt;&gt;&gt; next_word_keeper['test'] += 1
&gt;&gt;&gt; next_word_keeper
defaultdict(&lt;type 'int'&gt;, {'test': 2})
&gt;&gt;&gt; next_word_keeper['test2'] += 1
&gt;&gt;&gt; next_word_keeper
defaultdict(&lt;type 'int'&gt;, {'test': 2, 'test2': 1})


Using the above should get you:

def word_counts(f):
    f = f.lower().split()
    unique_f = list(set(f[:-1]))
    result = {}
    for word in unique_f:
        next_word_keeper = defaultdict(int)
        for i in range(len(f)-1):
            if word == f[i]:
                next_word_keeper[f[i + 1]] += 1
        result[word] = next_word_keeper
    return result




But this code is not the best when it comes to readability and performance!

Instead of going through the list multiple times, you can go though it once.
Using enumerate we get the current index, and then we can use it to get the next word.
And then using two defaultdicts we can simplify the function to six lines:

def word_counts(line):
    line = line.lower().split()
    results = defaultdict(lambda:defaultdict(int))
    for i, value in enumerate(line[:-1]):
        results[value][line[i + 1]] += 1
    return results


You can also go onto use the itertools pairwise recipe to further simplify the code.