For a Django App, each "member" is assigned a color to help identify them. Their color is stored in the database and then printed/copied into the HTML when it is needed. The only issue is that I am unsure how to generate random Hex colors in python/django. It's easy enough to generate RGB colors, but to store them I would either need to a) make three extra columns in my "Member" model or b) store them all in the same column and use commas to separate them, then, later, parse the colors for the HTML. Neither of these are very appealing, so, again, I'm wondering how to generate random Hex colors in python/django.
import random
r = lambda: random.randint(0,255)
print('#%02X%02X%02X' % (r(),r(),r()))
Here is a simple way:
import random
color = "%06x" % random.randint(0, 0xFFFFFF)
To generate a random 3 char color:
import random
color = "%03x" % random.randint(0, 0xFFF)
%x in C-based languages is a string formatter to format integers as hexadecimal strings while 0x is the prefix to write numbers in base-16.
Colors can be prefixed with "#" if needed (CSS style)
little late to the party,
import random
chars = '0123456789ABCDEF'
['#'+''.join(random.sample(chars,6)) for i in range(N)]
Store it as a HTML color value:
Updated: now accepts both integer (0-255) and float (0.0-1.0) arguments. These will be clamped to their allowed range.
def htmlcolor(r, g, b):
def _chkarg(a):
if isinstance(a, int): # clamp to range 0--255
if a < 0:
a = 0
elif a > 255:
a = 255
elif isinstance(a, float): # clamp to range 0.0--1.0 and convert to integer 0--255
if a < 0.0:
a = 0
elif a > 1.0:
a = 255
else:
a = int(round(a*255))
else:
raise ValueError('Arguments must be integers or floats.')
return a
r = _chkarg(r)
g = _chkarg(g)
b = _chkarg(b)
return '#{:02x}{:02x}{:02x}'.format(r,g,b)
Result:
In [14]: htmlcolor(250,0,0)
Out[14]: '#fa0000'
In [15]: htmlcolor(127,14,54)
Out[15]: '#7f0e36'
In [16]: htmlcolor(0.1, 1.0, 0.9)
Out[16]: '#19ffe5'
This has been done before. Rather than implementing this yourself, possibly introducing errors, you may want to use a ready library, for example Faker. Have a look at the color providers, in particular hex_digit.
In [1]: from faker import Factory
In [2]: fake = Factory.create()
In [3]: fake.hex_color()
Out[3]: u'#3cae6a'
In [4]: fake.hex_color()
Out[4]: u'#5a9e28'
Just store them as an integer with the three channels at different bit offsets (just like they are often stored in memory):
value = (red << 16) + (green << 8) + blue
(If each channel is 0-255). Store that integer in the database and do the reverse operation when you need to get back to the distinct channels.
import random
def hex_code_colors():
a = hex(random.randrange(0,256))
b = hex(random.randrange(0,256))
c = hex(random.randrange(0,256))
a = a[2:]
b = b[2:]
c = c[2:]
if len(a)<2:
a = "0" + a
if len(b)<2:
b = "0" + b
if len(c)<2:
c = "0" + c
z = a + b + c
return "#" + z.upper()
So many ways to do this, so here's a demo using "colorutils".
pip install colorutils
It is possible to generate random values in (RGB, HEX, WEB, YIQ, HSV).
# docs and downloads at
# https://pypi.python.org/pypi/colorutils/
from colorutils import random_web
from tkinter import Tk, Button
mgui = Tk()
mgui.geometry('150x28+400+200')
def rcolor():
rn = random_web()
print(rn) # for terminal watchers
cbutton.config(text=rn)
mgui.config(bg=rn)
cbutton = Button(text="Click", command=rcolor)
cbutton.pack()
mgui.mainloop()
I certainly hope that was helpful.
import secrets
# generate 4 sets of 2-digit hex chars for a color with transparency
rgba = f"#{secrets.token_hex(4)}" # example return: "#ffff0000"
# generate 3 sets of 2-digit hex chars for a non-alpha color
rgb = f"#{secrets.token_hex(3)}" # example return: "#ab12ce"
import random
def generate_color():
color = '#{:02x}{:02x}{:02x}'.format(*map(lambda x: random.randint(0, 255), range(3)))
return color
Basically, this will give you a hashtag, a randint that gets converted to hex, and a padding of zeroes.
from random import randint
color = '#{:06x}'.format(randint(0, 256**3))
#Use the colors wherever you need!
For generating random anything, take a look at the random module
I would suggest you use the module to generate a random integer, take it's modulo 2**24, and treat the top 8 bits as R, that middle 8 bits as G and the bottom 8 as B.
It can all be accomplished with div/mod or bitwise operations.
hex_digits = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
digit_array = []
for i in xrange(6):
digit_array.append(hex_digits[randint(0,15)])
joined_digits = ''.join(digit_array)
color = '#' + joined_digits
import random
def get_random_hex:
random_number = random.randint(0,16777215)
# convert to hexadecimal
hex_number = str(hex(random_number))
# remove 0x and prepend '#'
return'#'+ hex_number[2:]
Would like to improve upon this solution as I found that it could generate color codes that have less than 6 characters. I also wanted to generate a function that would create a list that can be used else where such as for clustering in matplotlib.
import random
def get_random_hex:
random_number = random.randint(0,16777215)
# convert to hexadecimal
hex_number = str(hex(random_number))
# remove 0x and prepend '#'
return'#'+ hex_number[2:]
My proposal is :
import numpy as np
def color_generator (no_colors):
colors = []
while len(colors) < no_colors:
random_number = np.random.randint(0,16777215)
hex_number = format(random_number, 'x')
if len(hex_number) == 6:
hex_number = '#'+ hex_number
colors.append (hex_number)
return colors
Here's a simple code that I wrote based on what hexadecimal color notations represent:
import random
def getRandomCol():
r = hex(random.randrange(0, 255))[2:]
g = hex(random.randrange(0, 255))[2:]
b = hex(random.randrange(0, 255))[2:]
random_col = '#'+r+g+b
return random_col
The '#' in the hexadecimal color code just represents that the number represented is just a hexadecimal number. What's important is the next 6 digits. Pairs of 2 digits in those 6 hexadecimal digits represent the intensity of RGB (Red, Green, and Blue) each. The intensity of each color ranges between 0-255 and a combination of different intensities of RGB produces different colors.
For example, in #ff00ff, the first ff is equivalent to 255 in decimal, the next 00 is equivalent to 0 in decimal, and the last ff is equivalent to 255 in decimal. Therefore, #ff00ff in hexadecimal color coding is equivalent to RGB(255, 0, 255).
With this concept, here's the explanation of my approach:
Generated intensities of random numbers for each of r, g
and b
Converted those intensities into hexadecimal
Ignored the first 2 characters of each hexadecimal value '0x'
Concatenated '#' with the hexadecimal values r, g and b
intensities.
Feel free to refer to this link if you wanna know more about how colors work: https://hackernoon.com/hex-colors-how-do-they-work-d8cb935ac0f
Cheers!
Hi, maybe i could help with the next function that generate random Hex colors :
from colour import Color
import random as random
def Hex_color():
L = '0123456789ABCDEF'
return Color('#'+ ''.join([random.choice(L) for i in range(6)][:]))
from random import randbytes
randbytes(3).hex()
output
f5f2c9
There are a lot of complex answers here, this is what I used for a code of mine using one import line and one line to get a random code:
import random
color = '#' + ''.join(random.choices('0123456789ABCDEF', k=6))
print(color)
The output will be something like:
#3F67CD
If you want to make a list of color values, let's say, of 10 random colors, you can do the following:
import random as r
hex_chars = '0123456789ABCDEF'
num_colors = 10
colors = ['#' + ''.join(r.choices(hex_chars, k=6)) for _ in range(num_colors)]
print(colors)
And the output will be a list containing ten different random colors:
['#3DBEA2', '#0B3B64', '#31D196', '#6A98C2', '#9C1712', '#73AFFE', '#9F5E0D', '#A2F07E', '#EB6407', '#7E8FB6']
I hope that helps!
Related
I am doing some data visualization with Python in Blender and I need to assign colors to the data being represented. There is too much data to spend time hand-picking colors for each, so I want to generate the colors pseudo-randomly - I would like a given string to always result in the same color. That way, if it appears more than once, it will be clear that it is the same item.
For example, given a list like ['Moose', 'Frog', 'Your Mother'], let's say Moose would always be maroon.
The colors are specified in RGB, where each channel is a float from 0.0 to 1.0.
Here's what I've tried so far that isn't working:
import random
import hashlib
def norm(x, min, max): # this normalization function has a problem
normalized = ( x - min(x) ) / (max(x) - min(x) )
return normalized
def r(string, val):
h = hash(string)
print(h)
if h < 0:
h = h * -1
rs = random.seed( int(h) + int(val) )
output = norm(rs, 0.0, 1.0)
return output
my_list = ['Moose', 'Frog', 'Your Mother']
item = my_list[0]
color = [ r(item,1), r(item,2), (item,1) ]
print(color)
It results in TypeError: 'float' object is not callable but I don't know why. I'm trying to normalize the way this answer demonstrates.
It might be best to have a list of possible colors, as it allows for control over the palette. Either way, I need a pseudo-random float in the range of 0.0 ~ 0.1.
You can try with this function. It returns a rgb value for the name by using the 3 first letters.
def name_color(name):
color = [(ord(c.lower())-97)*8 for c in name[:3]]
return color
It turns out I didn't need to normalize or do anything fancy. random() returns a range between 0.0 ~ 1.0 as it is. I just needed to take a closer look at how random.seed() is supposed to be used.
Here's my solution:
import random
import hashlib
def r(string, int): # pseudo-randomization function
h = hash( string + str(int) ) # hash string and int together
if h < 0: # ensure positive number
h = h * -1
random.seed(h) # set the seed to use for randomization
output = random.random() # produce random value in range 0.0 ~ 1.0
output = round(output, 6) # round to 6 decimal places (optional)
return output
my_list = ['Toyota', 'Tesla', 'Mercedes-Benz']
item = my_list[0] # choose which list item
color = [ r(item,0), r(item,1), r(item,2) ] # R,G,B values
print(item, color)
Output: Toyota [0.049121, 0.383824, 0.635146]
I may try the list approach later.
I'm new to Python and writing a small script to set an RGB color where two of the RGB colors are randint(0,255) and the third (selected randomly) is 0.
To acomplish this, I have this:
import time
from random import *
from neopixel import *
print ('Press Ctrl-C to quit.')
while True:
colorList = [0, randint(0,255), randint(0,255)]
shuffle(colorList)
rColor = ???
gColor = ???
bColor = ???
time.sleep(1)
I 'm having a lot of luck finding the answer but if I wanted to extract the first item from the list and set it to rColor, the second for gColor and etc, what Python function could I use to do that?
Edit: My end goal format would be an output that was simply "0, 150, 150". The NeoPixel library I am working with is picky so that is the literal format I am aiming for.
To set the color values to three variables. Use
r, g, b = colorList
or
r = colorList[0]
g = colorList[1]
b = colorList[2]
If the meaning of 'output' is print the result to console. Then use
print(*colorList, sep=', ')
If you need a string '0, 150, 150'
result = ', '.join(map(str, colorList))
or
result = ', '.join([str(x) for x in colorList])
This seems to do the trick.
from time import sleep
from random import randint, shuffle
while True:
color_list = [0, randint(0,255), randint(0,255)]
shuffle(color_list)
r_color, g_color, b_color = color_list
# sets colors to a string like "12, 10, 0"
colors = ', '.join(str(color) for color in color_list)
# print (colors)
sleep(1)
EDIT: added lines to include your edit
I have a numpy array
[2.15295647e+01, 8.12531501e+00, 3.97113829e+00, 1.00777250e+01]
and would like to format it so that it looks like this
[21.53, 08.13, 03.97, 10.08]
float_formatter = lambda x: "%.2f" % x
np.set_printoptions(formatter={'float_kind':float_formatter})
How can I adjust this to contain 2 float manipulations?
I need something like %02d %.2f % (x,y) but don't know how to change the lambda function to do this.
From what I understand, you just want your array values to be padded with two leading zeros and printed to two decimal places. All you need to do is modify the formatting string:
In [1]: "%05.2f" %np.pi
Out[1]: '03.14'
To break this down, the 5 means that the total length of the formatted string is 5 characters (including the decimal point), the 0 means that you want to pad with leading zeros, and the .2 means that you want two figures after the decimal point.
So if you wanted, say, 3 leading zeros and 4 decimal places you would do:
In [2]: "%08.4f" %np.pi
Out[2]: '003.1416
If you just wanted to pad with leading spaces rather than zeros, you can omit the 0 in front of the total length:
In [3]: "%8.4f" %np.pi
Out[3]: ' 3.1416'
There are other options that you can read about in the table of conversion flags here.
To set this as your new formatter, you would just do:
In [4]: float_formatter = lambda x: "%05.2f" %x
In [5]: np.set_printoptions(formatter={'float_kind':float_formatter})
If you want to work with two floats in your lambda, you need two input arguments like this:
float_formatter = lambda x, y: "%.2f %.2f" % (x, y)
You can define multiple inputs to lambda expressions, not just a single and the name is arbitrary. Here's the same thing with different argument names.
float_formatter = lambda float_one, float_two: "%.2f %.2f" % (float_one, float_two)
To change the values in the list you could use a list comprehension. Python 3 example below.
import numpy as np
precision = 2
formatter = "{0:."+str(precision)+"f}"
a = np.array([2.15295647e+01, 8.12531501e+00, 3.97113829e+00, 1.00777250e+01])
aFormat = np.array([float(formatter.format(i)) for i in a])
print(a, aFormat)
I first tried epileptic fish's option, but the printer function doesn't give you an array. It just gives you a float. So if you really want to change the print option for a 2d numpy matrix you will have to define your own function with an external counter on what to format which value with. The example below shows this with a class, because you don't want the external counter to be a global.
import numpy
class npPrint(object):
def __init__(self):
self.counter = 0
self.precision = 2
# end Constructor
def printer(self, x):
self.precision = 2
if self.counter == 0:
formatter = "{0:."+str(self.precision)+"f}"
self.counter += 1
else:
formatter = "{0:0"+str(self.precision)+"d}"
x = int(round(x))
self.counter = 0
return formatter.format(x)
# end printer
formatter = npPrint()
a = numpy.array([[2.15295647e+01, 8.12531501e+00], [3.97113829e+00, 1.00777250e+01]])
numpy.set_printoptions(formatter={"float_kind": lambda x, f=formatter: f.printer(x)})
print(a)
You have to have a counter, because you don't know if you are handed the x value or the y value. You just receive a float. This is really ugly. If I were you I would handle the printing myself and break apart the 2d matrix.
For my clustering gui, I am currently using random colors for the clusters, since I won't know before hand how many clusters I will end up with.
In Python, this looks like:
import random
def randomColor():
return (random.random(),random.random(),random.random())
However, when I update things, the colors change.
So what I would favor is to have a function which has an input argument I such as
def nonrandomColor(i):
...
return color
would always return the same color for the same I, while keeping the ability to generate arbitrarily many colors.
Answer does not have to be formulated in Python, it's more the general layout I'm interested in.
One way is to use caching. Use a defaultdict:
>>> import random
>>> def randomColor():
... return (random.random(),random.random(),random.random())
...
>>> from collections import defaultdict
>>> colors = defaultdict(randomColor)
>>> colors[3]
(0.10726172906719755, 0.97327604757295705, 0.58935794305308264)
>>> colors[1]
(0.48991106537516382, 0.77039712435566876, 0.73707003166893892)
>>> colors[3]
(0.10726172906719755, 0.97327604757295705, 0.58935794305308264)
Just set the seed of the random generator to the index, this might be cheaper than storing the colors.
random.seed(i)
Note that this will make random numbers way less random than before. If that is a problem, e.g. if your application uses random numbers elsewhere, you might want to look into the caching options suggested by other answers.
If you want repeatable non colliding colors then you could use something like the function below. It sections the number into 1, 10, 100 and then uses them as the RGB parts of the color.
def color(i):
r = i % 10
g = (i//10) % 10
b = (i//100) % 10
return(r*25, g*25, b*25)
For example:
color(1) == (25,0,0)
color(10) == (0,25,0)
color(999) = (225,225,255)
You want to store the colors in a dictionary or a list:
colors = {} # int -> color
def nonrandomColor(i):
if i not in colors:
colors[i] = randomColor()
return colors[i]
You can use i to seed the random number generator. So, as long as the seed remains the same, you get the same value.
>>> import random
>>> random.seed(12)
>>> random.randint(0,255), random.randint(0,255), random.randint(0,255)
(121, 168, 170)
>>> random.seed(12)
>>> random.randint(0,255), random.randint(0,255), random.randint(0,255)
(121, 168, 170)
>>> random.seed(10)
>>> random.randint(0,255), random.randint(0,255), random.randint(0,255)
(146, 109, 147)
>>> random.seed(10)
>>> random.randint(0,255), random.randint(0,255), random.randint(0,255)
(146, 109, 147)
Depending on the number of colours you're likely to generate (i.e., 10 or a million), the caching method might be better than the seed() method.
For example,
The function could be something like def RandABCD(n, .25, .34, .25, .25):
Where n is the length of the string to be generated and the following numbers are the desired probabilities of A, B, C, D.
I would imagine this is quite simple, however i am having trouble creating a working program. Any help would be greatly appreciated.
Here's the code to select a single weighted value. You should be able to take it from here. It uses bisect and random to accomplish the work.
from bisect import bisect
from random import random
def WeightedABCD(*weights):
chars = 'ABCD'
breakpoints = [sum(weights[:x+1]) for x in range(4)]
return chars[bisect(breakpoints, random())]
Call it like this: WeightedABCD(.25, .34, .25, .25).
EDIT: Here is a version that works even if the weights don't add up to 1.0:
from bisect import bisect_left
from random import uniform
def WeightedABCD(*weights):
chars = 'ABCD'
breakpoints = [sum(weights[:x+1]) for x in range(4)]
return chars[bisect_left(breakpoints, uniform(0.0,breakpoints[-1]))]
The random class is quite powerful in python. You can generate a list with the characters desired at the appropriate weights and then use random.choice to obtain a selection.
First, make sure you do an import random.
For example, let's say you wanted a truly random string from A,B,C, or D.
1. Generate a list with the characters
li = ['A','B','C','D']
Then obtain values from it using random.choice
output = "".join([random.choice(li) for i in range(0, n)])
You could easily make that a function with n as a parameter.
In the above case, you have an equal chance of getting A,B,C, or D.
You can use duplicate entries in the list to give characters higher probabilities. So, for example, let's say you wanted a 50% chance of A and 25% chances of B and C respectively. You could have an array like this:
li = ['A','A','B','C']
And so on.
It would not be hard to parameterize the characters coming in with desired weights, to model that I'd use a dictionary.
characterbasis = {'A':25, 'B':25, 'C':25, 'D':25}
Make that the first parameter, and the second being the length of the string and use the above code to generate your string.
For four letters, here's something quick off the top of my head:
from random import random
def randABCD(n, pA, pB, pC, pD):
# assumes pA + pB + pC + pD == 1
cA = pA
cB = cA + pB
cC = cB + pC
def choose():
r = random()
if r < cA:
return 'A'
elif r < cB:
return 'B'
elif r < cC:
return 'C'
else:
return 'D'
return ''.join([choose() for i in xrange(n)])
I have no doubt that this can be made much cleaner/shorter, I'm just in a bit of a hurry right now.
The reason I wouldn't be content with David in Dakota's answer of using a list of duplicate characters is that depending on your probabilities, it may not be possible to create a list with duplicates in the right numbers to simulate the probabilities you want. (Well, I guess it might always be possible, but you might wind up needing a huge list - what if your probabilities were 0.11235442079, 0.4072777384, 0.2297927874, 0.25057505341?)
EDIT: here's a much cleaner generic version that works with any number of letters with any weights:
from bisect import bisect
from random import uniform
def rand_string(n, content):
''' Creates a string of letters (or substrings) chosen independently
with specified probabilities. content is a dictionary mapping
a substring to its "weight" which is proportional to its probability,
and n is the desired number of elements in the string.
This does not assume the sum of the weights is 1.'''
l, cdf = zip(*[(l, w) for l, w in content.iteritems()])
cdf = list(cdf)
for i in xrange(1, len(cdf)):
cdf[i] += cdf[i - 1]
return ''.join([l[bisect(cdf, uniform(0, cdf[-1]))] for i in xrange(n)])
Here is a rough idea of what might suit you
import random as r
def distributed_choice(probs):
r= r.random()
cum = 0.0
for pair in probs:
if (r < cum + pair[1]):
return pair[0]
cum += pair[1]
The parameter probs takes a list of pairs of the form (object, probability). It is assumed that the sum of probabilities is 1 (otherwise, its trivial to normalize).
To use it just execute:
''.join([distributed_choice(probs)]*4)
Hmm, something like:
import random
class RandomDistribution:
def __init__(self, kv):
self.entries = kv.keys()
self.where = []
cnt = 0
for x in self.entries:
self.where.append(cnt)
cnt += kv[x]
self.where.append(cnt)
def find(self, key):
l, r = 0, len(self.where)-1
while l+1 < r:
m = (l+r)/2
if self.where[m] <= key:
l=m
else:
r=m
return self.entries[l]
def randomselect(self):
return self.find(random.random()*self.where[-1])
rd = RandomDistribution( {"foo": 5.5, "bar": 3.14, "baz": 2.8 } )
for x in range(1000):
print rd.randomselect()
should get you most of the way...
Thank you all for your help, I was able to figure something out, mostly with this info.
For my particular need, I did something like this:
import random
#Create a function to randomize a given string
def makerandom(seq):
return ''.join(random.sample(seq, len(seq)))
def randomDNA(n, probA=0.25, probC=0.25, probG=0.25, probT=0.25):
notrandom=''
A=int(n*probA)
C=int(n*probC)
T=int(n*probT)
G=int(n*probG)
#The remainder part here is used to make sure all n are used, as one cannot
#have half an A for example.
remainder=''
for i in range(0, n-(A+G+C+T)):
ramainder+=random.choice("ATGC")
notrandom=notrandom+ 'A'*A+ 'C'*C+ 'G'*G+ 'T'*T + remainder
return makerandom(notrandom)