I am stuck on figuring out how to map a pre-existing df of styling options to a df of integers:
import numpy as np
import pandas as pd
# Create a 9 x 3 array of integers
random_state = np.random.RandomState(seed=44)
myArray = random_state.randint(0,9, (9,3))
Which gives the output:
[[4 3 1]
[3 0 4]
[3 8 7]
[7 6 3]
[7 3 3]
[6 5 4]
[5 1 8]
[7 4 5]
[3 0 4]]
Then create a random 2D list of 'color: option' to be mapped onto myArray with the styler:
styleList = []
colorOptions = ['color: lime','color: red','color: yellow']
for i in arr:
inner = []
for j in i:
inner.append(np.random.choice(colorOptions))
styleList.append(inner)
for i in styleList: print(i)
Check the output:
['color: yellow', 'color: lime' , 'color: red']
['color: red' , 'color: lime' , 'color: red']
['color: yellow', 'color: yellow', 'color: yellow']
['color: yellow', 'color: red' , 'color: lime']
['color: yellow', 'color: red' , 'color: yellow']
['color: red' , 'color: lime' , 'color: yellow']
['color: red' , 'color: yellow', 'color: red']
['color: yellow', 'color: yellow', 'color: yellow']
['color: lime' , 'color: red' , 'color: red']
Convert both of these to dataframes:
df = pd.DataFrame(data=myArray, index=None, columns=['col1', 'col2', 'col3'])
dfStyle = pd.DataFrame(data=styleList, index=None, columns=['col1', 'col2', 'col3'])
Since I already have the dataframe dfStyle
with the styling options, how can I simply map its values to the integer values in df
(and without having to generate dfStyle
within a separate function)?
I'll avoid cluttering this space with my various attempts using df.style.apply()
, df.style.applymap()
and even functions that attempt to simply return dfStyle
, but this seemingly straightforward task has thrown me in circles.
Desired result is a styled df:
df[0][0]
should be the number 4 displayed with the color yellow
df[0][1]
should be the number 3 displayed with the color lime
df[0][2]
should be the number 1 displayed with the color red
etc.
Thanks for any help.
••• UPDATE •••
I figured out how to properly map the style as I wanted by using:
def color(row):
dfs = dfStyle.copy()
x = [dfs.iloc[row.name][j] for i,j in zip(row, dfStyle)]
return x
df.style.apply(color, axis=1)
which results in the desired:
After much prodding of my convoluted solution, I have come to the following (much cleaner) solutions for my original question.
I wasn't able to find these elsewhere on SO, so to anyone else who is interested:
Solution using apply(axis=0)
def f1(col): return [ dfStyle[col.name][i] for i in range(len(dfStyle)) ]
df.style.apply(f1, axis=0)
Solution using applymap()
via list comprehension
styleIterator = iter([ row for col in dfStyle for row in dfStyle[col] ] )
def f1(x): return next(styleIterator)
df.style.applymap(f1)
Solution using applymap()
via built-in functions
styleIterator = iter(dfStyle.values.flatten(order='F').tolist() )
# OR
styleIterator = iter(dfStyle.to_numpy().flatten(order='F').tolist() )
def f1(x): return next(styleIterator)
df.style.applymap(f1)
and, as provided by a friend, Solution using apply(axis=1)
with lambda
color = lambda row: [dfStyle.iloc[row.name][col] for col in dfStyle]
df.style.apply(color, axis=1)