Jekyll2018-02-27T20:27:30-05:00https://timcheeseman.com/funwithcomputers/Fun With ComputersComputers can be incredibly useful and incredibly silly. If we're lucky, this happens simultaneously.Tim CheesemanDo Androids Dream of Electric Blue?2017-12-21T20:00:00-05:002017-12-21T20:00:00-05:00https://timcheeseman.com/funwithcomputers/2017/12/21/do-androids-dream-of-electric-blue<p>Paint colors always have such fanciful names like “Flamingo’s Dream” and “Agreeable Gray”.
Can we teach a computer to invent new colors and give them fitting names? Let’s give it a shot!</p>
<h2 id="corpus-colorum">Corpus Colorum</h2>
<p>First, let’s gather some paint color information from existing brands.</p>
<p>Digging through the sources on color explorers for <a href="http://www.benjaminmoore.com/en-us/for-your-home/color-gallery">Benjamin Moore</a>, <a href="http://www.sherwin-williams.com/homeowners/color/find-and-explore-colors/paint-colors-by-family/">Sherwin-Williams</a>, and <a href="http://www.behr.com/consumer/colors/paint">Behr</a>, I was able to find some JSON endpoints that could give us names, RGB values, and color families for all of their currently available colors. There’s some other information (e.g. “color collection”, “goes great with”) that might be fun to play around with, but for now we’ll just grab this simple information, e.g.:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="s">'name'</span><span class="p">:</span> <span class="s">'SYLVAN MIST'</span><span class="p">,</span>
<span class="s">'rgb'</span><span class="p">:</span> <span class="p">(</span><span class="mi">184</span><span class="p">,</span> <span class="mi">199</span><span class="p">,</span> <span class="mi">191</span><span class="p">),</span>
<span class="s">'family'</span><span class="p">:</span> <span class="s">'BLUE'</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If data gathering and cleaning sounds boring to you, skip ahead to <a href="#exploring-the-corpus">Exploring the Corpus</a>.</p>
<p>We’ll define a few utility functions as we go along to help us homogenize the data.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ALLOWED_SPECIAL_CHARS</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="s">' </span><span class="se">\'</span><span class="s">"-.,&?'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">clean_name</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<span class="s">"""Strip superfluous characters and convert to uppercase"""</span>
<span class="k">return</span> <span class="s">''</span><span class="o">.</span><span class="n">join</span><span class="p">([</span>
<span class="n">c</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">n</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">isalnum</span><span class="p">()</span> <span class="ow">or</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">ALLOWED_SPECIAL_CHARS</span><span class="p">])</span>
</code></pre></div></div>
<p>The Benjamin Moore API gives us data pretty close to what we need.
We’ll just have to convert the RGB values from hexadecimal and clean up the names.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="k">def</span> <span class="nf">rgb_from_hex</span><span class="p">(</span><span class="n">h</span><span class="p">):</span>
<span class="s">"""Convert hex string (e.g. "00FF00") to tuple of RGB values (e.g. (0, 255, 0))"""</span>
<span class="k">return</span> <span class="n">struct</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">'BBB'</span><span class="p">,</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">h</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">load_benjamin_moore</span><span class="p">():</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'https://www.benjaminmoore.com/api/colors'</span><span class="p">)</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="s">'colors'</span><span class="p">]</span><span class="o">.</span><span class="n">values</span><span class="p">()),</span>
<span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">'name'</span><span class="p">,</span> <span class="s">'family'</span><span class="p">,</span> <span class="s">'hex'</span><span class="p">])</span>
<span class="n">df</span><span class="p">[[</span><span class="s">'name'</span><span class="p">,</span> <span class="s">'family'</span><span class="p">]]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[[</span><span class="s">'name'</span><span class="p">,</span> <span class="s">'family'</span><span class="p">]]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">clean_name</span><span class="p">))</span>
<span class="n">df</span><span class="p">[</span><span class="s">'rgb'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'hex'</span><span class="p">]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">rgb_from_hex</span><span class="p">)</span>
<span class="k">return</span> <span class="n">df</span><span class="o">.</span><span class="n">drop</span><span class="p">(</span><span class="s">'hex'</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">benjamin_moore_colors</span> <span class="o">=</span> <span class="n">load_benjamin_moore</span><span class="p">()</span>
<span class="nb">len</span><span class="p">(</span><span class="n">benjamin_moore_colors</span><span class="p">)</span>
</code></pre></div></div>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>4221
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">benjamin_moore_colors</span><span class="o">.</span><span class="n">sample</span><span class="p">()</span>
</code></pre></div></div>
<table>
<thead>
<tr>
<th> </th>
<th style="text-align: right">name</th>
<th style="text-align: right">family</th>
<th style="text-align: right">rgb</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>3378</strong></td>
<td style="text-align: right">WORN LEATHER SHOES</td>
<td style="text-align: right">NEUTRAL</td>
<td style="text-align: right">(152, 142, 120)</td>
</tr>
</tbody>
</table>
<p>Sherwin Williams is also close, but for some reason the RGB values are given as a single integer.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">rgb_from_dec</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="s">"""Convert integer (e.g. 65280) to tuple of RGB values (e.g. (0, 255, 0))"""</span>
<span class="k">return</span> <span class="n">rgb_from_hex</span><span class="p">(</span><span class="n">f</span><span class="s">'{d:06x}'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">load_sherwin_williams</span><span class="p">():</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'https://www.sherwin-williams.com/color-visualization/services/color/SW/all'</span><span class="p">)</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">'name'</span><span class="p">,</span> <span class="s">'colorFamilyNames'</span><span class="p">,</span> <span class="s">'rgb'</span><span class="p">])</span>
<span class="n">df</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">clean_name</span><span class="p">)</span>
<span class="n">df</span><span class="p">[</span><span class="s">'family'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'colorFamilyNames'</span><span class="p">]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">clean_name</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
<span class="n">df</span><span class="p">[</span><span class="s">'rgb'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'rgb'</span><span class="p">]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">rgb_from_dec</span><span class="p">)</span>
<span class="k">return</span> <span class="n">df</span><span class="o">.</span><span class="n">drop</span><span class="p">(</span><span class="s">'colorFamilyNames'</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">sherwin_williams_colors</span> <span class="o">=</span> <span class="n">load_sherwin_williams</span><span class="p">()</span>
<span class="nb">len</span><span class="p">(</span><span class="n">sherwin_williams_colors</span><span class="p">)</span>
</code></pre></div></div>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1746
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sherwin_williams_colors</span><span class="o">.</span><span class="n">sample</span><span class="p">()</span>
</code></pre></div></div>
<table>
<thead>
<tr>
<th> </th>
<th style="text-align: right">name</th>
<th style="text-align: right">family</th>
<th style="text-align: right">rgb</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>190</strong></td>
<td style="text-align: right">AMBITIOUS AMBER</td>
<td style="text-align: right">ORANGE</td>
<td style="text-align: right">(240, 203, 151)</td>
</tr>
</tbody>
</table>
<p>Behr is a bit tricky as the data is inside of a JavaScript source file instead of a JSON endpoint.
Also, the color family data is stored separately from the color information, so we’ll have to join the two together.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">itertools</span>
<span class="k">def</span> <span class="nf">get_data_list</span><span class="p">(</span><span class="n">js_source</span><span class="p">):</span>
<span class="s">"""
Extract the JSON string representing a list from a JavaScript
source file of the form 'var data = [ ... ];'
"""</span>
<span class="k">return</span> <span class="n">js_source</span><span class="p">[</span><span class="n">js_source</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s">'['</span><span class="p">)</span> <span class="p">:</span> <span class="n">js_source</span><span class="o">.</span><span class="n">rfind</span><span class="p">(</span><span class="s">']'</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">flatten_groups</span><span class="p">(</span><span class="n">groups</span><span class="p">):</span>
<span class="s">"""
For some reason, groups are stored as a list of lists of strings
(which are themselves comma-separated lists of color IDs). Flatten
this into a single list of unique color IDs.
For example, [['a,b,c', 'd,e'], ['f,g', 'h,i,j'], ['k', 'a,b']]
would become ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
"""</span>
<span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span>
<span class="n">itertools</span><span class="o">.</span><span class="n">chain</span><span class="o">.</span><span class="n">from_iterable</span><span class="p">(</span>
<span class="n">x</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">','</span><span class="p">)</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">itertools</span><span class="o">.</span><span class="n">chain</span><span class="o">.</span><span class="n">from_iterable</span><span class="p">(</span><span class="n">groups</span><span class="p">))))</span>
<span class="k">def</span> <span class="nf">load_behr</span><span class="p">():</span>
<span class="n">colors_data</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'http://www.behr.com/mainService/services/colornx/all.js'</span><span class="p">)</span><span class="o">.</span><span class="n">text</span>
<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">read_json</span><span class="p">(</span><span class="n">get_data_list</span><span class="p">(</span><span class="n">colors_data</span><span class="p">))</span>
<span class="c"># extract first row as column names</span>
<span class="n">df</span><span class="o">.</span><span class="n">columns</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">df</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">reindex</span><span class="p">(</span><span class="n">df</span><span class="o">.</span><span class="n">index</span><span class="o">.</span><span class="n">drop</span><span class="p">(</span><span class="mi">0</span><span class="p">))[[</span><span class="s">'id'</span><span class="p">,</span> <span class="s">'name'</span><span class="p">,</span> <span class="s">'rgb'</span><span class="p">]]</span>
<span class="n">family_data</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'http://www.behr.com/mainService/services/xml/families.js'</span><span class="p">)</span><span class="o">.</span><span class="n">text</span>
<span class="n">family_df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">read_json</span><span class="p">(</span><span class="n">get_data_list</span><span class="p">(</span><span class="n">family_data</span><span class="p">))</span>
<span class="n">family_df</span><span class="p">[</span><span class="s">'groups'</span><span class="p">]</span> <span class="o">=</span> <span class="n">family_df</span><span class="p">[</span><span class="s">'groups'</span><span class="p">]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">flatten_groups</span><span class="p">)</span>
<span class="c"># explode `groups` column into a column for each value in the list</span>
<span class="c"># e.g. {'name': 'Red', 'groups': ['a', 'b', 'c']}</span>
<span class="c"># becomes {'name': 'Red', '0': 'a', '1': 'b', '2': 'c']}</span>
<span class="n">family_df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">concat</span><span class="p">([</span><span class="n">family_df</span><span class="p">[</span><span class="s">'name'</span><span class="p">],</span>
<span class="n">family_df</span><span class="p">[</span><span class="s">'groups'</span><span class="p">]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">pd</span><span class="o">.</span><span class="n">Series</span><span class="p">)],</span>
<span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="c"># melt group columns into a single column, creating a row from each</span>
<span class="c"># e.g. {'name': 'Red', '0': 'a', '1': 'b', '2': 'c']}</span>
<span class="c"># becomes {'name': 'Red', 'id': 'a'}, {'name': 'Red', 'id': 'b'}, {'name': 'Red', 'id': 'c'}</span>
<span class="n">family_df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">melt</span><span class="p">(</span><span class="n">family_df</span><span class="p">,</span>
<span class="n">id_vars</span><span class="o">=</span><span class="p">[</span><span class="s">'name'</span><span class="p">],</span>
<span class="n">value_name</span><span class="o">=</span><span class="s">'id'</span><span class="p">)[[</span><span class="s">'name'</span><span class="p">,</span> <span class="s">'id'</span><span class="p">]]</span><span class="o">.</span><span class="n">dropna</span><span class="p">()</span>
<span class="c"># join families to colors by ID</span>
<span class="n">df</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">merge</span><span class="p">(</span><span class="n">family_df</span><span class="p">,</span> <span class="n">on</span><span class="o">=</span><span class="s">'id'</span><span class="p">,</span> <span class="n">suffixes</span><span class="o">=</span><span class="p">[</span><span class="s">'_color'</span><span class="p">,</span> <span class="s">'_family'</span><span class="p">])</span>
<span class="n">df</span><span class="p">[[</span><span class="s">'name'</span><span class="p">,</span> <span class="s">'family'</span><span class="p">]]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[[</span><span class="s">'name_color'</span><span class="p">,</span> <span class="s">'name_family'</span><span class="p">]]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">clean_name</span><span class="p">))</span>
<span class="n">df</span><span class="p">[</span><span class="s">'rgb'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s">'rgb'</span><span class="p">]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">rgb_from_hex</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">:]))</span>
<span class="k">return</span> <span class="n">df</span><span class="p">[[</span><span class="s">'name'</span><span class="p">,</span> <span class="s">'family'</span><span class="p">,</span> <span class="s">'rgb'</span><span class="p">]]</span>
<span class="n">behr_colors</span> <span class="o">=</span> <span class="n">load_behr</span><span class="p">()</span>
<span class="nb">len</span><span class="p">(</span><span class="n">behr_colors</span><span class="p">)</span>
</code></pre></div></div>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2891
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">behr_colors</span><span class="o">.</span><span class="n">sample</span><span class="p">()</span>
</code></pre></div></div>
<table>
<thead>
<tr>
<th> </th>
<th style="text-align: right">name</th>
<th style="text-align: right">family</th>
<th style="text-align: right">rgb</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>1650</strong></td>
<td style="text-align: right">DRIED CHAMOMILE</td>
<td style="text-align: right">YELLOW</td>
<td style="text-align: right">(209, 179, 117)</td>
</tr>
</tbody>
</table>
<p>Now that they’re all in the same format, we can combine them all together.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">colors</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">concat</span><span class="p">([</span><span class="n">benjamin_moore_colors</span><span class="p">,</span>
<span class="n">sherwin_williams_colors</span><span class="p">,</span>
<span class="n">behr_colors</span><span class="p">])</span><span class="o">.</span><span class="n">drop_duplicates</span><span class="p">()</span>
<span class="nb">len</span><span class="p">(</span><span class="n">colors</span><span class="p">)</span>
</code></pre></div></div>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>8137
</code></pre></div></div>
<p>There are a bunch of colors with weird family names like ‘Timeless Color’ or ‘Historic Color’.
For simplicity, let’s discard these.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">COLOR_FAMILIES</span> <span class="o">=</span> <span class="nb">set</span><span class="p">([</span>
<span class="s">'RED'</span><span class="p">,</span> <span class="s">'ORANGE'</span><span class="p">,</span> <span class="s">'PINK'</span><span class="p">,</span> <span class="s">'BROWN'</span><span class="p">,</span> <span class="s">'NEUTRAL'</span><span class="p">,</span> <span class="s">'GRAY'</span><span class="p">,</span>
<span class="s">'WHITE'</span><span class="p">,</span> <span class="s">'YELLOW'</span><span class="p">,</span> <span class="s">'PURPLE'</span><span class="p">,</span> <span class="s">'BLUE'</span><span class="p">,</span> <span class="s">'BLACK'</span><span class="p">,</span> <span class="s">'GREEN'</span><span class="p">])</span>
<span class="n">colors</span> <span class="o">=</span> <span class="n">colors</span><span class="p">[</span>
<span class="n">colors</span><span class="p">[</span><span class="s">'family'</span><span class="p">]</span><span class="o">.</span><span class="n">isin</span><span class="p">(</span><span class="n">COLOR_FAMILIES</span><span class="p">)]</span>
<span class="nb">len</span><span class="p">(</span><span class="n">colors</span><span class="p">)</span>
</code></pre></div></div>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>7405
</code></pre></div></div>
<p><a id="exploring-the-corpus"></a></p>
<h3 id="exploring-the-corpus">Exploring the Corpus</h3>
<p>It would be neat if we could view the colors inline.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">IPython.display</span> <span class="kn">import</span> <span class="n">HTML</span>
<span class="k">def</span> <span class="nf">display_color</span><span class="p">(</span><span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="n">HTML</span><span class="p">(</span><span class="s">"""
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">{name}</div>{family}</p>
<p><svg width="64" height="64" style="background: #{rgb_hex}" /></p>
<p>#{rgb_hex}</p>
</div>
"""</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
<span class="n">name</span><span class="o">=</span><span class="n">color</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
<span class="n">family</span><span class="o">=</span><span class="n">color</span><span class="o">.</span><span class="n">family</span><span class="p">,</span>
<span class="n">rgb_hex</span><span class="o">=</span><span class="n">struct</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'BBB'</span><span class="p">,</span> <span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span><span class="o">.</span><span class="nb">hex</span><span class="p">()))</span>
<span class="k">def</span> <span class="nf">display_colors</span><span class="p">(</span><span class="n">colors_df</span><span class="p">):</span>
<span class="k">return</span> <span class="n">HTML</span><span class="p">(</span><span class="s">"""
<div>
{colors}
</div>
"""</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
<span class="n">colors</span> <span class="o">=</span> <span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
<span class="n">display_color</span><span class="p">(</span><span class="n">c</span><span class="p">)</span><span class="o">.</span><span class="n">data</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">colors_df</span><span class="o">.</span><span class="n">itertuples</span><span class="p">())))</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">display_colors</span><span class="p">(</span><span class="n">colors</span><span class="o">.</span><span class="n">sample</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
</code></pre></div></div>
<div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">EMERGENCY ZONE</div>ORANGE</p>
<p><svg width="64" height="64" style="background: #e36841" /></p>
<p>#e36841</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">PAR FOUR</div>GREEN</p>
<p><svg width="64" height="64" style="background: #d2d6c7" /></p>
<p>#d2d6c7</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">HACIENDA BLUE</div>BLUE</p>
<p><svg width="64" height="64" style="background: #0087a9" /></p>
<p>#0087a9</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">HIGHLAND THISTLE</div>RED</p>
<p><svg width="64" height="64" style="background: #b9a0b0" /></p>
<p>#b9a0b0</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">PASSION VINE</div>GREEN</p>
<p><svg width="64" height="64" style="background: #888169" /></p>
<p>#888169</p>
</div>
</div>
<p>Let’s see which families have the most shades:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">colors</span><span class="o">.</span><span class="n">groupby</span><span class="p">(</span><span class="s">'family'</span><span class="p">)</span><span class="o">.</span><span class="n">count</span><span class="p">()</span> \
<span class="o">.</span><span class="n">rename</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="p">{</span><span class="s">'name'</span><span class="p">:</span> <span class="s">'count'</span><span class="p">})</span> \
<span class="o">.</span><span class="n">sort_values</span><span class="p">(</span><span class="s">'count'</span><span class="p">,</span> <span class="n">ascending</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span> \
<span class="o">.</span><span class="n">reset_index</span><span class="p">()[[</span><span class="s">'family'</span><span class="p">,</span> <span class="s">'count'</span><span class="p">]]</span>
</code></pre></div></div>
<div>
<table>
<thead>
<tr style="text-align: right;">
<th></th>
<th>family</th>
<th>count</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>GREEN</td>
<td>1145</td>
</tr>
<tr>
<th>1</th>
<td>BLUE</td>
<td>995</td>
</tr>
<tr>
<th>2</th>
<td>RED</td>
<td>938</td>
</tr>
<tr>
<th>3</th>
<td>ORANGE</td>
<td>862</td>
</tr>
<tr>
<th>4</th>
<td>YELLOW</td>
<td>766</td>
</tr>
<tr>
<th>5</th>
<td>BROWN</td>
<td>755</td>
</tr>
<tr>
<th>6</th>
<td>PURPLE</td>
<td>650</td>
</tr>
<tr>
<th>7</th>
<td>GRAY</td>
<td>600</td>
</tr>
<tr>
<th>8</th>
<td>WHITE</td>
<td>339</td>
</tr>
<tr>
<th>9</th>
<td>NEUTRAL</td>
<td>287</td>
</tr>
<tr>
<th>10</th>
<td>BLACK</td>
<td>51</td>
</tr>
<tr>
<th>11</th>
<td>PINK</td>
<td>17</td>
</tr>
</tbody>
</table>
</div>
<p>Who knew there could be 51 Shades of Black?</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">display_colors</span><span class="p">(</span><span class="n">colors</span><span class="p">[</span><span class="n">colors</span><span class="p">[</span><span class="s">'family'</span><span class="p">]</span> <span class="o">==</span> <span class="s">'BLACK'</span><span class="p">]</span><span class="o">.</span><span class="n">sample</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span>
</code></pre></div></div>
<div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">TWILIGHT ZONE</div>BLACK</p>
<p><svg width="64" height="64" style="background: #2f3234" /></p>
<p>#2f3234</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">DEEP CAVIAR</div>BLACK</p>
<p><svg width="64" height="64" style="background: #453f3f" /></p>
<p>#453f3f</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">CHEATING HEART</div>BLACK</p>
<p><svg width="64" height="64" style="background: #494c4d" /></p>
<p>#494c4d</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">BLACK SATIN</div>BLACK</p>
<p><svg width="64" height="64" style="background: #2a2d2e" /></p>
<p>#2a2d2e</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">ABYSS</div>BLACK</p>
<p><svg width="64" height="64" style="background: #3f4348" /></p>
<p>#3f4348</p>
</div>
</div>
<p>There are some colors with more than one name. Let’s take a look at the one with the most names.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">most_common_rgb</span> <span class="o">=</span> <span class="n">colors</span><span class="p">[</span><span class="n">colors</span><span class="o">.</span><span class="n">duplicated</span><span class="p">([</span><span class="s">'rgb'</span><span class="p">],</span> <span class="n">keep</span><span class="o">=</span><span class="bp">False</span><span class="p">)]</span> \
<span class="o">.</span><span class="n">groupby</span><span class="p">(</span><span class="s">'rgb'</span><span class="p">)</span><span class="o">.</span><span class="n">count</span><span class="p">()</span> \
<span class="o">.</span><span class="n">rename</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="p">{</span><span class="s">'name'</span><span class="p">:</span> <span class="s">'count'</span><span class="p">})</span> \
<span class="o">.</span><span class="n">nlargest</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">[</span><span class="s">'count'</span><span class="p">])</span> \
<span class="o">.</span><span class="n">reset_index</span><span class="p">()</span><span class="o">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="s">'rgb'</span><span class="p">]</span>
<span class="n">display_colors</span><span class="p">(</span><span class="n">colors</span><span class="p">[</span><span class="n">colors</span><span class="p">[</span><span class="s">'rgb'</span><span class="p">]</span> <span class="o">==</span> <span class="n">most_common_rgb</span><span class="p">])</span>
</code></pre></div></div>
<div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">WHITE SWAN</div>NEUTRAL</p>
<p><svg width="64" height="64" style="background: #ebe4d0" /></p>
<p>#ebe4d0</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">SPANISH WHITE</div>WHITE</p>
<p><svg width="64" height="64" style="background: #ebe4d0" /></p>
<p>#ebe4d0</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">GRAND TETON WHITE</div>NEUTRAL</p>
<p><svg width="64" height="64" style="background: #ebe4d0" /></p>
<p>#ebe4d0</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">BATTENBERG</div>NEUTRAL</p>
<p><svg width="64" height="64" style="background: #ebe4d0" /></p>
<p>#ebe4d0</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">MONTEREY WHITE</div>NEUTRAL</p>
<p><svg width="64" height="64" style="background: #ebe4d0" /></p>
<p>#ebe4d0</p>
</div>
</div>
<h2 id="all-in-the-family">All in the Family</h2>
<p>A good start for assigning a name to a random color would be to first figure out to which family it belongs.</p>
<p>We’re going to try a few different classifiers, so let’s wrap them in a similar interface:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">sklearn.utils</span> <span class="kn">import</span> <span class="n">shuffle</span>
<span class="k">class</span> <span class="nc">ColorFamilyClassifier</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color_df</span><span class="p">,</span> <span class="n">train_percent</span><span class="o">=</span><span class="mf">0.8</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">shuffled</span> <span class="o">=</span> <span class="n">shuffle</span><span class="p">(</span><span class="n">color_df</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">get_features</span><span class="p">(</span><span class="n">color</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_label</span><span class="p">(</span><span class="n">color</span><span class="p">))</span>
<span class="k">for</span> <span class="n">color</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">shuffled</span><span class="o">.</span><span class="n">itertuples</span><span class="p">()</span>
<span class="p">]</span>
<span class="n">cut_index</span> <span class="o">=</span> <span class="nb">round</span><span class="p">(</span><span class="n">train_percent</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">train_set</span> <span class="o">=</span> <span class="n">data</span><span class="p">[:</span><span class="n">cut_index</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">test_set</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">cut_index</span><span class="p">:]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">init_classifier</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">raise</span> <span class="nb">NotImplemented</span>
<span class="k">def</span> <span class="nf">get_label</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">raise</span> <span class="nb">NotImplemented</span>
<span class="k">def</span> <span class="nf">init_classifier</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">raise</span> <span class="nb">NotImplemented</span>
<span class="k">def</span> <span class="nf">accuracy</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">raise</span> <span class="nb">NotImplemented</span>
<span class="k">def</span> <span class="nf">classify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">raise</span> <span class="nb">NotImplemented</span>
</code></pre></div></div>
<h3 id="naïve-bayes">Naïve Bayes</h3>
<p>Let’s start by seeing how much mileage we can get with a <a href="https://en.wikipedia.org/wiki/Naive_Bayes_classifier">Naïve Bayes classifier</a> using RGB values as features.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">nltk</span>
<span class="k">class</span> <span class="nc">NaiveBayesClassifier</span><span class="p">(</span><span class="n">ColorFamilyClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_label</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="n">color</span><span class="o">.</span><span class="n">family</span>
<span class="k">def</span> <span class="nf">init_classifier</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">classifier</span> <span class="o">=</span> <span class="n">nltk</span><span class="o">.</span><span class="n">NaiveBayesClassifier</span><span class="o">.</span><span class="n">train</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">train_set</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">accuracy</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">nltk</span><span class="o">.</span><span class="n">classify</span><span class="o">.</span><span class="n">accuracy</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">classifier</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">test_set</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">classify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">classifier</span><span class="o">.</span><span class="n">classify</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">get_features</span><span class="p">(</span><span class="n">color</span><span class="p">))</span>
<span class="k">class</span> <span class="nc">NaiveBayesRGBClassifier</span><span class="p">(</span><span class="n">NaiveBayesClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">((</span><span class="s">"red"</span><span class="p">,</span> <span class="s">"green"</span><span class="p">,</span> <span class="s">"blue"</span><span class="p">),</span> <span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">))</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">classifier</span> <span class="o">=</span> <span class="n">NaiveBayesRGBClassifier</span><span class="p">(</span><span class="n">colors</span><span class="p">)</span>
<span class="n">classifier</span><span class="o">.</span><span class="n">accuracy</span><span class="p">()</span>
</code></pre></div></div>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0.26806212018906145
</code></pre></div></div>
<p>That’s not very good accuracy. Let’s try different colorspaces:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">colorsys</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">husl</span>
<span class="kn">from</span> <span class="nn">colormath.color_conversions</span> <span class="kn">import</span> <span class="n">convert_color</span>
<span class="kn">from</span> <span class="nn">colormath.color_objects</span> <span class="kn">import</span> <span class="n">CMYKColor</span><span class="p">,</span> <span class="n">LabColor</span><span class="p">,</span> <span class="n">sRGBColor</span>
<span class="c"># we'll run each classifier multiple times and look at the</span>
<span class="c"># mean and standard deviation over all of the runs</span>
<span class="n">RUNS_PER_CLASSIFIER</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">class</span> <span class="nc">NaiveBayesHSVClassifier</span><span class="p">(</span><span class="n">NaiveBayesClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="n">hsv</span> <span class="o">=</span> <span class="n">colorsys</span><span class="o">.</span><span class="n">rgb_to_hsv</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">((</span><span class="s">"hue"</span><span class="p">,</span> <span class="s">"saturation"</span><span class="p">,</span> <span class="s">"value"</span><span class="p">),</span> <span class="n">hsv</span><span class="p">))</span>
<span class="k">class</span> <span class="nc">NaiveBayesHLSClassifier</span><span class="p">(</span><span class="n">NaiveBayesClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="n">hls</span> <span class="o">=</span> <span class="n">colorsys</span><span class="o">.</span><span class="n">rgb_to_hls</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">((</span><span class="s">"hue"</span><span class="p">,</span> <span class="s">"lightness"</span><span class="p">,</span> <span class="s">"saturation"</span><span class="p">),</span> <span class="n">hls</span><span class="p">))</span>
<span class="k">class</span> <span class="nc">NaiveBayesHUSLClassifier</span><span class="p">(</span><span class="n">NaiveBayesClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="n">hsl</span> <span class="o">=</span> <span class="n">husl</span><span class="o">.</span><span class="n">rgb_to_husl</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">((</span><span class="s">"hue"</span><span class="p">,</span> <span class="s">"saturation"</span><span class="p">,</span> <span class="s">"lightness"</span><span class="p">),</span> <span class="n">hsl</span><span class="p">))</span>
<span class="k">class</span> <span class="nc">NaiveBayesCMYKClassifier</span><span class="p">(</span><span class="n">NaiveBayesClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="n">rgb</span> <span class="o">=</span> <span class="n">sRGBColor</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="n">cmyk</span> <span class="o">=</span> <span class="n">convert_color</span><span class="p">(</span><span class="n">rgb</span><span class="p">,</span> <span class="n">CMYKColor</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">((</span><span class="s">"cyan"</span><span class="p">,</span> <span class="s">"magenta"</span><span class="p">,</span> <span class="s">"yellow"</span><span class="p">,</span> <span class="s">"black"</span><span class="p">),</span> <span class="p">(</span><span class="nb">getattr</span><span class="p">(</span><span class="n">cmyk</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">CMYKColor</span><span class="o">.</span><span class="n">VALUES</span><span class="p">)))</span>
<span class="k">class</span> <span class="nc">NaiveBayesLabClassifier</span><span class="p">(</span><span class="n">NaiveBayesClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="n">rgb</span> <span class="o">=</span> <span class="n">sRGBColor</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="n">lab</span> <span class="o">=</span> <span class="n">convert_color</span><span class="p">(</span><span class="n">rgb</span><span class="p">,</span> <span class="n">LabColor</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">((</span><span class="s">"lightness"</span><span class="p">,</span> <span class="s">"green-red"</span><span class="p">,</span> <span class="s">"blue-yellow"</span><span class="p">),</span> <span class="p">(</span><span class="nb">getattr</span><span class="p">(</span><span class="n">lab</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">LabColor</span><span class="o">.</span><span class="n">VALUES</span><span class="p">)))</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">results</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">c</span><span class="p">(</span><span class="n">colors</span><span class="p">)</span><span class="o">.</span><span class="n">accuracy</span><span class="p">())</span>
<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="p">[</span>
<span class="n">NaiveBayesCMYKClassifier</span><span class="p">,</span> <span class="n">NaiveBayesHLSClassifier</span><span class="p">,</span> <span class="n">NaiveBayesHSVClassifier</span><span class="p">,</span>
<span class="n">NaiveBayesHUSLClassifier</span><span class="p">,</span> <span class="n">NaiveBayesLabClassifier</span><span class="p">,</span> <span class="n">NaiveBayesRGBClassifier</span><span class="p">]</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">RUNS_PER_CLASSIFIER</span><span class="p">)</span>
<span class="p">]</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">results</span><span class="p">,</span> <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">'Classifier'</span><span class="p">,</span> <span class="s">'Accuracy'</span><span class="p">])</span> \
<span class="o">.</span><span class="n">groupby</span><span class="p">(</span><span class="s">'Classifier'</span><span class="p">)</span> \
<span class="o">.</span><span class="n">agg</span><span class="p">({</span><span class="s">'Accuracy'</span><span class="p">:</span> <span class="p">[</span><span class="s">'mean'</span><span class="p">,</span> <span class="s">'std'</span><span class="p">]})</span> \
<span class="o">.</span><span class="n">reset_index</span><span class="p">()</span> \
<span class="o">.</span><span class="n">sort_values</span><span class="p">((</span><span class="s">'Accuracy'</span><span class="p">,</span> <span class="s">'mean'</span><span class="p">),</span> <span class="n">ascending</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
</code></pre></div></div>
<div>
<table>
<thead>
<tr>
<th></th>
<th>Classifier</th>
<th colspan="2" halign="left">Accuracy</th>
</tr>
<tr>
<th></th>
<th></th>
<th>mean</th>
<th>std</th>
</tr>
</thead>
<tbody>
<tr>
<th>0</th>
<td>NaiveBayesCMYKClassifier</td>
<td>0.403241</td>
<td>0.010765</td>
</tr>
<tr>
<th>2</th>
<td>NaiveBayesHSVClassifier</td>
<td>0.370155</td>
<td>0.010850</td>
</tr>
<tr>
<th>1</th>
<td>NaiveBayesHLSClassifier</td>
<td>0.360702</td>
<td>0.001464</td>
</tr>
<tr>
<th>5</th>
<td>NaiveBayesRGBClassifier</td>
<td>0.262255</td>
<td>0.007603</td>
</tr>
<tr>
<th>4</th>
<td>NaiveBayesLabClassifier</td>
<td>0.226604</td>
<td>0.011524</td>
</tr>
<tr>
<th>3</th>
<td>NaiveBayesHUSLClassifier</td>
<td>0.225928</td>
<td>0.010810</td>
</tr>
</tbody>
</table>
</div>
<p>~40% still isn’t great. Let’s try a different kind of classifier.</p>
<h3 id="k-nearest-neighbor">k-Nearest Neighbor</h3>
<p>Let’s try <a href="https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm">k-NN</a> classifiers over these colorspaces and values of N.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">from</span> <span class="nn">sklearn.neighbors</span> <span class="kn">import</span> <span class="n">KNeighborsClassifier</span>
<span class="k">class</span> <span class="nc">KNNColorClassifier</span><span class="p">(</span><span class="n">ColorFamilyClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color_corpus</span><span class="p">,</span> <span class="n">train_percent</span><span class="o">=</span><span class="mf">0.8</span><span class="p">,</span> <span class="n">n_neighbors</span><span class="o">=</span><span class="mi">5</span><span class="p">):</span>
<span class="c"># labels in the KNeighborsClassifier are integers, so we'll create</span>
<span class="c"># a unique integer label for each color family and map both ways for convenience</span>
<span class="n">families</span> <span class="o">=</span> <span class="n">color_corpus</span><span class="p">[</span><span class="s">'family'</span><span class="p">]</span><span class="o">.</span><span class="n">unique</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">family_map</span> <span class="o">=</span> <span class="p">{</span><span class="n">f</span><span class="p">:</span> <span class="n">i</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">f</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">families</span><span class="p">)}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">reverse_family_map</span> <span class="o">=</span> <span class="p">{</span><span class="n">v</span><span class="p">:</span> <span class="n">k</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">family_map</span><span class="o">.</span><span class="n">items</span><span class="p">()}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">n_neighbors</span> <span class="o">=</span> <span class="n">n_neighbors</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="n">color_corpus</span><span class="p">,</span> <span class="n">train_percent</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_label</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">family_map</span><span class="p">[</span><span class="n">color</span><span class="o">.</span><span class="n">family</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">init_classifier</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">classifier</span> <span class="o">=</span> <span class="n">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">n_neighbors</span><span class="p">)</span>
<span class="p">[</span><span class="n">features</span><span class="p">,</span> <span class="n">labels</span><span class="p">]</span> <span class="o">=</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">train_set</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">classifier</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">features</span><span class="p">,</span> <span class="n">labels</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">accuracy</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="p">[</span><span class="n">features</span><span class="p">,</span> <span class="n">labels</span><span class="p">]</span> <span class="o">=</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="bp">self</span><span class="o">.</span><span class="n">test_set</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">classifier</span><span class="o">.</span><span class="n">score</span><span class="p">(</span><span class="n">features</span><span class="p">,</span> <span class="n">labels</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">classify</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">reverse_family_map</span><span class="p">[</span>
<span class="bp">self</span><span class="o">.</span><span class="n">classifier</span><span class="o">.</span><span class="n">predict</span><span class="p">(</span>
<span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">get_features</span><span class="p">(</span><span class="n">color</span><span class="p">)]</span>
<span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="p">]</span>
<span class="k">def</span> <span class="nf">get_neighbors</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">shuffled</span><span class="o">.</span><span class="n">iloc</span><span class="p">[[</span>
<span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">classifier</span><span class="o">.</span><span class="n">kneighbors</span><span class="p">(</span>
<span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">get_features</span><span class="p">(</span><span class="n">color</span><span class="p">))</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span>
<span class="n">return_distance</span><span class="o">=</span><span class="bp">False</span>
<span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="p">]]</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># we'll try values of n_neighbors in this range</span>
<span class="n">MIN_N</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">MAX_N</span> <span class="o">=</span> <span class="mi">20</span>
<span class="k">class</span> <span class="nc">KNNRGBClassifier</span><span class="p">(</span><span class="n">KNNColorClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="n">color</span><span class="o">.</span><span class="n">rgb</span>
<span class="k">class</span> <span class="nc">KNNHSVClassifier</span><span class="p">(</span><span class="n">KNNColorClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="n">colorsys</span><span class="o">.</span><span class="n">rgb_to_hsv</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">KNNHLSClassifier</span><span class="p">(</span><span class="n">KNNColorClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="n">colorsys</span><span class="o">.</span><span class="n">rgb_to_hls</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">KNNHUSLClassifier</span><span class="p">(</span><span class="n">KNNColorClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="k">return</span> <span class="n">husl</span><span class="o">.</span><span class="n">rgb_to_husl</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">KNNCMYKClassifier</span><span class="p">(</span><span class="n">KNNColorClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="n">rgb</span> <span class="o">=</span> <span class="n">sRGBColor</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="n">cmyk</span> <span class="o">=</span> <span class="n">convert_color</span><span class="p">(</span><span class="n">rgb</span><span class="p">,</span> <span class="n">CMYKColor</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span><span class="nb">getattr</span><span class="p">(</span><span class="n">cmyk</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">CMYKColor</span><span class="o">.</span><span class="n">VALUES</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">KNNLabClassifier</span><span class="p">(</span><span class="n">KNNColorClassifier</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">get_features</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="n">rgb</span> <span class="o">=</span> <span class="n">sRGBColor</span><span class="p">(</span><span class="o">*</span><span class="n">color</span><span class="o">.</span><span class="n">rgb</span><span class="p">)</span>
<span class="n">lab</span> <span class="o">=</span> <span class="n">convert_color</span><span class="p">(</span><span class="n">rgb</span><span class="p">,</span> <span class="n">LabColor</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span><span class="nb">getattr</span><span class="p">(</span><span class="n">lab</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">LabColor</span><span class="o">.</span><span class="n">VALUES</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">results</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">c</span><span class="p">(</span><span class="n">colors</span><span class="p">,</span> <span class="n">n_neighbors</span><span class="o">=</span><span class="n">n</span><span class="p">)</span><span class="o">.</span><span class="n">accuracy</span><span class="p">())</span>
<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="p">[</span>
<span class="n">KNNCMYKClassifier</span><span class="p">,</span> <span class="n">KNNHLSClassifier</span><span class="p">,</span> <span class="n">KNNHSVClassifier</span><span class="p">,</span>
<span class="n">KNNHUSLClassifier</span><span class="p">,</span> <span class="n">KNNLabClassifier</span><span class="p">,</span> <span class="n">KNNRGBClassifier</span><span class="p">]</span>
<span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">MIN_N</span><span class="p">,</span> <span class="n">MAX_N</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">RUNS_PER_CLASSIFIER</span><span class="p">)</span>
<span class="p">]</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">results</span><span class="p">,</span> <span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">'Classifier'</span><span class="p">,</span> <span class="s">'N'</span><span class="p">,</span> <span class="s">'Accuracy'</span><span class="p">])</span> \
<span class="o">.</span><span class="n">groupby</span><span class="p">([</span><span class="s">'Classifier'</span><span class="p">,</span> <span class="s">'N'</span><span class="p">])</span> \
<span class="o">.</span><span class="n">agg</span><span class="p">({</span><span class="s">'Accuracy'</span><span class="p">:</span> <span class="p">[</span><span class="s">'mean'</span><span class="p">,</span> <span class="s">'std'</span><span class="p">]})</span> \
<span class="o">.</span><span class="n">reset_index</span><span class="p">()</span> \
<span class="o">.</span><span class="n">sort_values</span><span class="p">((</span><span class="s">'Accuracy'</span><span class="p">,</span> <span class="s">'mean'</span><span class="p">),</span> <span class="n">ascending</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span> \
<span class="o">.</span><span class="n">head</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</code></pre></div></div>
<div>
<table>
<thead>
<tr>
<th></th>
<th>Classifier</th>
<th>N</th>
<th colspan="2" halign="left">Accuracy</th>
</tr>
<tr>
<th></th>
<th></th>
<th></th>
<th>mean</th>
<th>std</th>
</tr>
</thead>
<tbody>
<tr>
<th>95</th>
<td>KNNLabClassifier</td>
<td>16</td>
<td>0.742606</td>
<td>0.004173</td>
</tr>
<tr>
<th>93</th>
<td>KNNLabClassifier</td>
<td>14</td>
<td>0.737205</td>
<td>0.006377</td>
</tr>
<tr>
<th>99</th>
<td>KNNLabClassifier</td>
<td>20</td>
<td>0.735044</td>
<td>0.004930</td>
</tr>
<tr>
<th>88</th>
<td>KNNLabClassifier</td>
<td>9</td>
<td>0.734504</td>
<td>0.006691</td>
</tr>
<tr>
<th>96</th>
<td>KNNLabClassifier</td>
<td>17</td>
<td>0.734099</td>
<td>0.006909</td>
</tr>
</tbody>
</table>
</div>
<p>It looks like the <a href="https://en.wikipedia.org/wiki/Lab_color_space">Lab colorspace</a> was the most accurate, and 16 neighbors seems to have slightly outperformed other values in our range.</p>
<p>~74% accuracy should be “good enough” for our purposes.
Let’s put this classifier to work!</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">classifier</span> <span class="o">=</span> <span class="n">KNNLabClassifier</span><span class="p">(</span><span class="n">colors</span><span class="p">,</span> <span class="n">train_percent</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">n_neighbors</span><span class="o">=</span><span class="mi">16</span><span class="p">)</span>
</code></pre></div></div>
<h3 id="testing-out-the-classifier">Testing out the Classifier</h3>
<p>Let’s see how far off the classifier is when it’s wrong. Is it pretty close (e.g. classifying an orange as a red or a yellow) or way off (e.g. classifying a blue as a pink)?</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">classified</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">(</span>
<span class="p">((</span><span class="n">c</span><span class="o">.</span><span class="n">family</span><span class="p">,</span> <span class="n">classifier</span><span class="o">.</span><span class="n">classify</span><span class="p">(</span><span class="n">c</span><span class="p">))</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">colors</span><span class="o">.</span><span class="n">itertuples</span><span class="p">()),</span>
<span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">'Expected'</span><span class="p">,</span> <span class="s">'Actual'</span><span class="p">])</span>
<span class="n">totals</span> <span class="o">=</span> <span class="n">classified</span><span class="o">.</span><span class="n">groupby</span><span class="p">([</span><span class="s">'Expected'</span><span class="p">])</span><span class="o">.</span><span class="n">size</span><span class="p">()</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">classified</span><span class="o">.</span><span class="n">groupby</span><span class="p">([</span><span class="s">'Expected'</span><span class="p">,</span> <span class="s">'Actual'</span><span class="p">])</span><span class="o">.</span><span class="n">size</span><span class="p">()</span><span class="o">.</span><span class="n">reset_index</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">'Count'</span><span class="p">)</span>
<span class="n">results</span><span class="p">[</span><span class="s">'Pct'</span><span class="p">]</span> <span class="o">=</span> <span class="n">results</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[</span><span class="s">'Count'</span><span class="p">]</span> <span class="o">/</span> <span class="n">totals</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="s">'Expected'</span><span class="p">]],</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>
<p>First, let’s see which families the classifier most accurately identifies.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">results</span><span class="p">[</span><span class="n">results</span><span class="p">[</span><span class="s">'Expected'</span><span class="p">]</span> <span class="o">==</span> <span class="n">results</span><span class="p">[</span><span class="s">'Actual'</span><span class="p">]]</span> \
<span class="o">.</span><span class="n">sort_values</span><span class="p">(</span><span class="s">'Pct'</span><span class="p">,</span> <span class="n">ascending</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
</code></pre></div></div>
<div>
<table>
<thead>
<tr style="text-align: right;">
<th></th>
<th>Expected</th>
<th>Actual</th>
<th>Count</th>
<th>Pct</th>
</tr>
</thead>
<tbody>
<tr>
<th>32</th>
<td>GREEN</td>
<td>GREEN</td>
<td>996</td>
<td>0.869869</td>
</tr>
<tr>
<th>69</th>
<td>RED</td>
<td>RED</td>
<td>812</td>
<td>0.865672</td>
</tr>
<tr>
<th>5</th>
<td>BLUE</td>
<td>BLUE</td>
<td>836</td>
<td>0.840201</td>
</tr>
<tr>
<th>50</th>
<td>ORANGE</td>
<td>ORANGE</td>
<td>680</td>
<td>0.788863</td>
</tr>
<tr>
<th>62</th>
<td>PURPLE</td>
<td>PURPLE</td>
<td>504</td>
<td>0.775385</td>
</tr>
<tr>
<th>79</th>
<td>WHITE</td>
<td>WHITE</td>
<td>262</td>
<td>0.772861</td>
</tr>
<tr>
<th>23</th>
<td>GRAY</td>
<td>GRAY</td>
<td>448</td>
<td>0.746667</td>
</tr>
<tr>
<th>87</th>
<td>YELLOW</td>
<td>YELLOW</td>
<td>556</td>
<td>0.725849</td>
</tr>
<tr>
<th>11</th>
<td>BROWN</td>
<td>BROWN</td>
<td>538</td>
<td>0.712583</td>
</tr>
<tr>
<th>0</th>
<td>BLACK</td>
<td>BLACK</td>
<td>35</td>
<td>0.686275</td>
</tr>
<tr>
<th>41</th>
<td>NEUTRAL</td>
<td>NEUTRAL</td>
<td>75</td>
<td>0.261324</td>
</tr>
</tbody>
</table>
</div>
<p>Next, we’ll take a look at each family and see which family it is most commonly incorrectly identified as.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">results</span><span class="p">[</span><span class="n">results</span><span class="p">[</span><span class="s">'Expected'</span><span class="p">]</span> <span class="o">!=</span> <span class="n">results</span><span class="p">[</span><span class="s">'Actual'</span><span class="p">]]</span> \
<span class="o">.</span><span class="n">sort_values</span><span class="p">(</span><span class="s">'Pct'</span><span class="p">,</span> <span class="n">ascending</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span> \
<span class="o">.</span><span class="n">drop_duplicates</span><span class="p">(</span><span class="s">'Expected'</span><span class="p">)</span>
</code></pre></div></div>
<div>
<table>
<thead>
<tr style="text-align: right;">
<th></th>
<th>Expected</th>
<th>Actual</th>
<th>Count</th>
<th>Pct</th>
</tr>
</thead>
<tbody>
<tr>
<th>56</th>
<td>PINK</td>
<td>RED</td>
<td>14</td>
<td>0.823529</td>
</tr>
<tr>
<th>39</th>
<td>NEUTRAL</td>
<td>GRAY</td>
<td>89</td>
<td>0.310105</td>
</tr>
<tr>
<th>2</th>
<td>BLACK</td>
<td>GRAY</td>
<td>9</td>
<td>0.176471</td>
</tr>
<tr>
<th>81</th>
<td>YELLOW</td>
<td>BROWN</td>
<td>93</td>
<td>0.121410</td>
</tr>
<tr>
<th>47</th>
<td>ORANGE</td>
<td>BROWN</td>
<td>66</td>
<td>0.076566</td>
</tr>
<tr>
<th>19</th>
<td>BROWN</td>
<td>YELLOW</td>
<td>49</td>
<td>0.064901</td>
</tr>
<tr>
<th>60</th>
<td>PURPLE</td>
<td>GRAY</td>
<td>42</td>
<td>0.064615</td>
</tr>
<tr>
<th>7</th>
<td>BLUE</td>
<td>GREEN</td>
<td>60</td>
<td>0.060302</td>
</tr>
<tr>
<th>65</th>
<td>RED</td>
<td>BROWN</td>
<td>55</td>
<td>0.058635</td>
</tr>
<tr>
<th>21</th>
<td>GRAY</td>
<td>BLUE</td>
<td>35</td>
<td>0.058333</td>
</tr>
<tr>
<th>75</th>
<td>WHITE</td>
<td>NEUTRAL</td>
<td>15</td>
<td>0.044248</td>
</tr>
<tr>
<th>35</th>
<td>GREEN</td>
<td>YELLOW</td>
<td>44</td>
<td>0.038428</td>
</tr>
</tbody>
</table>
</div>
<p>This makes sense as we have very few datapoints in the pink family, neutral and gray have a lot of overlap, and most of the paint colors in the black family are actually gray.</p>
<p>For now let’s just accept this as “good enough” and have some fun. Let’s generate a some random RGB values and take a guess at the family to which it belongs.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span>
<span class="k">def</span> <span class="nf">random_colors</span><span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
<span class="k">return</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">({</span>
<span class="s">'name'</span><span class="p">:</span> <span class="s">'???'</span><span class="p">,</span>
<span class="s">'family'</span><span class="p">:</span> <span class="s">'???'</span><span class="p">,</span>
<span class="s">'rgb'</span><span class="p">:</span> <span class="p">(</span>
<span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">),</span>
<span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">),</span>
<span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">),</span>
<span class="p">)</span>
<span class="p">}</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">i</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">classify_colors</span><span class="p">(</span><span class="n">df</span><span class="p">):</span>
<span class="k">return</span> <span class="n">df</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">classifier</span><span class="o">.</span><span class="n">classify</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">new_colors</span> <span class="o">=</span> <span class="n">random_colors</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="n">new_colors</span><span class="p">[</span><span class="s">'family'</span><span class="p">]</span> <span class="o">=</span> <span class="n">classify_colors</span><span class="p">(</span><span class="n">new_colors</span><span class="p">)</span>
<span class="n">display_colors</span><span class="p">(</span><span class="n">new_colors</span><span class="p">)</span>
</code></pre></div></div>
<div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">???</div>GREEN</p>
<p><svg width="64" height="64" style="background: #228c42" /></p>
<p>#228c42</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">???</div>BLUE</p>
<p><svg width="64" height="64" style="background: #42d5f0" /></p>
<p>#42d5f0</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">???</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #826f9f" /></p>
<p>#826f9f</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">???</div>GREEN</p>
<p><svg width="64" height="64" style="background: #64e599" /></p>
<p>#64e599</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">???</div>BROWN</p>
<p><svg width="64" height="64" style="background: #4d360b" /></p>
<p>#4d360b</p>
</div>
</div>
<h1 id="desert-rose-by-any-other-name">‘Desert Rose’ by Any Other Name</h1>
<p>Now that we can make a decent guess towards the color family for a random RGB value, let’s try to build off of the existing color names for similar colors to create fun new names.</p>
<p>Let’s start by creating our random mystery color.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mystery_color</span> <span class="o">=</span> <span class="n">new_colors</span><span class="o">.</span><span class="n">sample</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">mystery_color</span><span class="p">[</span><span class="s">'family'</span><span class="p">]</span> <span class="o">=</span> <span class="n">classify_colors</span><span class="p">(</span><span class="n">mystery_color</span><span class="p">)</span>
<span class="n">display_colors</span><span class="p">(</span><span class="n">mystery_color</span><span class="p">)</span>
</code></pre></div></div>
<div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">???</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #826f9f" /></p>
<p>#826f9f</p>
</div>
</div>
<p>Let’s look at the closest named colors.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">neighbors</span> <span class="o">=</span> <span class="n">classifier</span><span class="o">.</span><span class="n">get_neighbors</span><span class="p">(</span><span class="n">mystery_color</span><span class="o">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">display_colors</span><span class="p">(</span><span class="n">neighbors</span><span class="p">)</span>
</code></pre></div></div>
<div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">CLEMATIS</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #7e6596" /></p>
<p>#7e6596</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">FORGET-ME-NOT</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #716998" /></p>
<p>#716998</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">PURPLE AGATE</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #8c7eaf" /></p>
<p>#8c7eaf</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">PURPLE PARADISE</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #79669e" /></p>
<p>#79669e</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">CHARMED VIOLET</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #8b7eb1" /></p>
<p>#8b7eb1</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">ROMANTIC MOMENT</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #8f76af" /></p>
<p>#8f76af</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">SECOND POUR</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #887ca5" /></p>
<p>#887ca5</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">NOTORIOUS</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #7b658b" /></p>
<p>#7b658b</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">LILAC INTUITION</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #997ea8" /></p>
<p>#997ea8</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">VIOLET VIXEN</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #74688c" /></p>
<p>#74688c</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">UNIMAGINABLE</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #8b7eba" /></p>
<p>#8b7eba</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">SEDUCTION</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #655f8e" /></p>
<p>#655f8e</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">NAPLES SUNSET</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #987ea4" /></p>
<p>#987ea4</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">CROCUS PETAL PURPLE</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #9487ba" /></p>
<p>#9487ba</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">KIMONO VIOLET</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #75769c" /></p>
<p>#75769c</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">CLASSIC WALTZ</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #71588d" /></p>
<p>#71588d</p>
</div>
</div>
<h2 id="on-your-markov-get-set-go">On your Markov, get set, go!</h2>
<p>To generate names for our mystery color, let’s try training a <a href="https://en.wikipedia.org/wiki/Markov_chain">Markov chain</a> on not only the names of these closest colors, but the product of all synonyms of all component words within the names to give us more variety. We’ll limit synonyms by part of speech so the generated names make slightly more sense.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">nltk.corpus</span> <span class="kn">import</span> <span class="n">wordnet</span>
<span class="kn">import</span> <span class="nn">spacy</span> <span class="c"># faster than wordnet for tokenizing and part-of-speech tagging</span>
<span class="n">nlp</span> <span class="o">=</span> <span class="n">spacy</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s">"en"</span><span class="p">)</span>
<span class="c"># map spaCy POS to WordNet</span>
<span class="n">POS_MAP</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">'ADJ'</span><span class="p">:</span> <span class="s">'a'</span><span class="p">,</span>
<span class="s">'ADV'</span><span class="p">:</span> <span class="s">'r'</span><span class="p">,</span>
<span class="s">'NOUN'</span><span class="p">:</span> <span class="s">'n'</span><span class="p">,</span>
<span class="s">'VERB'</span><span class="p">:</span> <span class="s">'v'</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">def</span> <span class="nf">get_syns</span><span class="p">(</span><span class="n">token</span><span class="p">):</span>
<span class="s">"""get synonyms for a spaCy token"""</span>
<span class="n">synsets</span> <span class="o">=</span> <span class="n">wordnet</span><span class="o">.</span><span class="n">synsets</span><span class="p">(</span><span class="n">token</span><span class="o">.</span><span class="n">orth_</span><span class="p">,</span> <span class="n">pos</span><span class="o">=</span><span class="n">POS_MAP</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">token</span><span class="o">.</span><span class="n">pos_</span><span class="p">))</span>
<span class="k">if</span> <span class="n">synsets</span><span class="p">:</span>
<span class="k">return</span> <span class="n">itertools</span><span class="o">.</span><span class="n">chain</span><span class="o">.</span><span class="n">from_iterable</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">lemma_names</span><span class="p">()</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">synsets</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="n">token</span><span class="o">.</span><span class="n">orth_</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">explode</span><span class="p">(</span><span class="n">color_name</span><span class="p">):</span>
<span class="s">"""explode a color name into the product of all of its component words' synonyms"""</span>
<span class="k">return</span> <span class="nb">set</span><span class="p">(</span>
<span class="s">' '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">variant</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'_'</span><span class="p">,</span> <span class="s">' '</span><span class="p">)</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
<span class="k">for</span> <span class="n">variant</span> <span class="ow">in</span> <span class="n">itertools</span><span class="o">.</span><span class="n">product</span><span class="p">(</span>
<span class="o">*</span><span class="p">(</span><span class="n">get_syns</span><span class="p">(</span><span class="n">token</span><span class="p">)</span> <span class="k">for</span> <span class="n">token</span> <span class="ow">in</span> <span class="n">nlp</span><span class="p">(</span><span class="n">color_name</span><span class="o">.</span><span class="n">lower</span><span class="p">()))))</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">string</span>
<span class="kn">import</span> <span class="nn">markovify</span>
<span class="k">def</span> <span class="nf">make_markov_model</span><span class="p">(</span><span class="n">colors</span><span class="p">):</span>
<span class="k">return</span> <span class="n">markovify</span><span class="o">.</span><span class="n">Text</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="c"># we're pre-parsing the sentences</span>
<span class="n">parsed_sentences</span><span class="o">=</span><span class="p">[</span>
<span class="n">variant</span><span class="o">.</span><span class="n">split</span><span class="p">()</span>
<span class="k">for</span> <span class="n">variant</span> <span class="ow">in</span> <span class="nb">set</span><span class="p">(</span>
<span class="n">itertools</span><span class="o">.</span><span class="n">chain</span><span class="o">.</span><span class="n">from_iterable</span><span class="p">(</span>
<span class="n">colors</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">explode</span><span class="p">)</span><span class="o">.</span><span class="n">values</span><span class="p">))</span>
<span class="p">])</span>
<span class="k">def</span> <span class="nf">name_color</span><span class="p">(</span><span class="n">color</span><span class="p">):</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">make_markov_model</span><span class="p">(</span><span class="n">classifier</span><span class="o">.</span><span class="n">get_neighbors</span><span class="p">(</span><span class="n">color</span><span class="p">))</span>
<span class="k">return</span> <span class="n">string</span><span class="o">.</span><span class="n">capwords</span><span class="p">(</span>
<span class="n">model</span><span class="o">.</span><span class="n">make_sentence</span><span class="p">(</span>
<span class="c"># we're generating short names and don't care about overlap with original text</span>
<span class="n">test_output</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">max_words</span><span class="o">=</span><span class="mi">3</span><span class="p">))</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mystery_color</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span> <span class="o">=</span> <span class="n">name_color</span><span class="p">(</span><span class="n">mystery_color</span><span class="o">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">display_colors</span><span class="p">(</span><span class="n">mystery_color</span><span class="p">)</span>
</code></pre></div></div>
<div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Over-embellished Eden</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #826f9f" /></p>
<p>#826f9f</p>
</div>
</div>
<p>Not bad for a computer. Let’s try it some more!</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">new_colors</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span> <span class="o">=</span> <span class="n">new_colors</span><span class="o">.</span><span class="nb">apply</span><span class="p">(</span><span class="n">name_color</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">display_colors</span><span class="p">(</span><span class="n">new_colors</span><span class="p">)</span>
</code></pre></div></div>
<div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Unmediated Dark-green</div>GREEN</p>
<p><svg width="64" height="64" style="background: #228c42" /></p>
<p>#228c42</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Queen Story Drab</div>BLUE</p>
<p><svg width="64" height="64" style="background: #42d5f0" /></p>
<p>#42d5f0</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Lilac Suspicion</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #826f9f" /></p>
<p>#826f9f</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Branch Brook Dark-green</div>GREEN</p>
<p><svg width="64" height="64" style="background: #64e599" /></p>
<p>#64e599</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Crisp Browned</div>BROWN</p>
<p><svg width="64" height="64" style="background: #4d360b" /></p>
<p>#4d360b</p>
</div>
</div>
<p>Most of the generated names will be nonsensical (and many also NSFW), but I did come across a few good ones. Here are the highlights:</p>
<div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Unprompted Empurpled</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #6e3281" /></p>
<p>#6e3281</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Million Dollar Marxist</div>RED</p>
<p><svg width="64" height="64" style="background: #b70a1f" /></p>
<p>#b70a1f</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Induce Watercourse</div>BLUE</p>
<p><svg width="64" height="64" style="background: #10cffb" /></p>
<p>#10cffb</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Sharp-worded American Cheddar</div>ORANGE</p>
<p><svg width="64" height="64" style="background: #e77616" /></p>
<p>#e77616</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Summertime Sorry</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #8fb0eb" /></p>
<p>#8fb0eb</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Graeco-roman Chocolate-brown</div>BROWN</p>
<p><svg width="64" height="64" style="background: #52382e" /></p>
<p>#52382e</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Cat Valium</div>GREEN</p>
<p><svg width="64" height="64" style="background: #5bfee2" /></p>
<p>#5bfee2</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Unconscionable Orange Tree</div>ORANGE</p>
<p><svg width="64" height="64" style="background: #c44312" /></p>
<p>#c44312</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Unused Butter</div>YELLOW</p>
<p><svg width="64" height="64" style="background: #f0d299" /></p>
<p>#f0d299</p>
</div>
<div style="width: 148px; display: inline-block">
<p><div style="font-weight: bold">Scandalmongering Shuttlecock</div>YELLOW</p>
<p><svg width="64" height="64" style="background: #e8c467" /></p>
<p>#e8c467</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Norse Naughty</div>PURPLE</p>
<p><svg width="64" height="64" style="background: #5d04f2" /></p>
<p>#5d04f2</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Disconsolate Denim</div>BLUE</p>
<p><svg width="64" height="64" style="background: #74b7c5" /></p>
<p>#74b7c5</p>
</div>
<div style="width: 148px; display: inline-block">
<p><div style="font-weight: bold">Italian Methamphetamine Green</div>GREEN</p>
<p><svg width="64" height="64" style="background: #84e8e9" /></p>
<p>#84e8e9</p>
</div>
<div style="width: 128px; display: inline-block">
<p><div style="font-weight: bold">Odoriferous & Off-key</div>YELLOW</p>
<p><svg width="64" height="64" style="background: #d7b743" /></p>
<p>#d7b743</p>
</div>
</div>
<h2 id="journeys-end-bac9d6">Journey’s End (#BAC9D6)</h2>
<p>For now it’s time to climb back out of the rabbit hole, but maybe one day we can <a href="https://arxiv.org/pdf/1704.08224.pdf">teach our algorithm about puns</a> (or even just include homophones in addition to synonyms to increase the likelihood of accidental puns).</p>
<p>Thanks for humoring me and go have some <a href="https://timcheeseman.com/funwithcomputers/">fun with computers</a>.</p>Tim CheesemanPaint colors always have such fanciful names like “Flamingo’s Dream” and “Agreeable Gray”. Can we teach a computer to invent new colors and give them fitting names? Let’s give it a shot!