Je cherche à colorer une table de valeurs dans Excel 2010 en fonction de leur valeur absolue. Fondamentalement, si j'ai la table:
... les cellules sont colorées par la valeur brute de la cellule. Ce que je voudrais faire, c’est colorer par la valeur absolue de la cellule, donc avec la coloration de la cellule de ce tableau:
... mais avec les valeurs du premier tableau (les valeurs réelles). Des idées sur la façon dont on pourrait faire cela? Via l'interface graphique ou avec VBA?
Je ne pense pas qu'il existe un moyen de faire cela avec trois couleurs (rouge, jaune, vert), mais vous pouvez le faire avec deux couleurs (par exemple jaune et vert). Faites simplement la même couleur pour la valeur basse et la couleur pour la valeur haute. De cette façon, les cellules avec la valeur absolue la plus basse auront la couleur du milieu et les cellules avec la valeur absolue la plus haute auront l’autre couleur.
Voici ma solution à ce problème. La formule de format conditionnel lit
=AND(ABS(B3)>0,ABS(B3)<=500)
pour le vert le plus foncé, l'échelle passe de 500 à 1 000, de 1 000 à 1 500 et enfin de 1 500 à 2 000 pour la bande rouge.
Voici une photo de l'ensemble de données que j'ai utilisé pour tester ces formats conditionnels:
Une variante de cette illustration de mise en forme conditionnelle simple peut fonctionner pour vous.
Mettez en surbrillance l’ensemble de la plage de données (la cellule LH supérieure doit être le point d’ancrage de l’adressage relatif) et entrez la formule: en «notation relative», c’est-à-dire les références de cellule sans le signe dollar. Vous devez également tenir compte de l'ordre des règles.
La formule la plus haute est masquée mais lit =(ABS(B3)>39) * (ABS(B3)<41)
Notez que le symbole * applique une opération AND.
Ok, j'ai une solution qui fonctionne avec le conditionnement 3 couleurs. Fondamentalement, vous fournissez une région à mon code. Il crée ensuite deux plages, une de nombres négatifs et une de valeurs positives. Il applique ensuite la mise en forme conditionnelle
rouge-bas jaune-moyen vert-haut à la plage positive et
rouge-haut jaune-moyen vert-bas jusqu'à la plage négative.
C’était une solution rapide, donc elle était bâclée et peu robuste (par exemple, elle ne fonctionne que dans les colonnes A à Z en raison d’une conversion ascii paresseuse pour les numéros de colonne), mais cela fonctionne. (Je posterais une photo mais je n'ai pas assez de points)
---------------------modifier---------------------------- ---
@pnuts a raison, à moins que les données soient symétriques, cette solution ne fonctionnera pas telle quelle. C'est pourquoi j'ai proposé une nouvelle solution. Je vais d’abord expliquer l’idée générale, puis, en gros, vider le code. Si vous comprenez la logique, le code devrait être assez clair. C'est une solution plutôt compliquée pour un problème aussi simple en apparence, mais n'est-ce pas toujours le cas? :-P
Nous continuons d’utiliser l’idée de base du code original, de créer une plage négative et de lui appliquer une échelle de couleurs, puis de créer une plage positive et de lui appliquer l’échelle de couleur inversée. Comme vu ci-dessous
Négatif ........... 0 ................ positif
vert jaune rouge | rouge jaune vert
Donc, avec nos données asymétriques data_set = {- 1, -1, -2, -2, -2, -2, -3, -4,1,5,8,13} ce que je fais est le reflet de la la valeur extrême. Dans ce cas 13, donc maintenant data_set = {- 13, -1, -1, -2, -2, -2, -2, -3, -4,1,5,8,13} Notice l'élément additionnel -13 . Je suppose que vous avez un bouton pour activer cette macro, donc je stocke le supplément -13 dans une cellule située sous le bouton, de sorte que même si elle n’est pas visible (ouais, je sais qu’ils peuvent déplacer le bouton, etc., mais c’était la chose la plus facile à laquelle je pouvais penser)
Bien, tout cela est bien et les cartes vertes correspondent aux cartes 13 ET -13, mais le dégradé de couleurs est basé sur les centiles (en fait, le code à barres des couleurs utilise le 50e centile pour déterminer le point médian, ou dans notre cas, où la section jaune est)
Selection.FormatConditions(1).ColorScaleCriteria(2).Value = 50
donc avec notre distribution {-13, -1, -1, -2, -2, -2, -2, -3, -4,1,5,8,13}, nous pourrions commencer à voir le jaune dans la plage positive autour du nombre 8.5 Depuis 8.5 est le 50ème percentile. mais dans la plage négative (même si nous ajoutons un -13 en miroir), le 50e centile est égal à -2, de sorte que notre jaune dans la plage négative commence à 2 !! À peine idéal. tout comme les pnuts mentionnés, mais nous nous rapprochons. si vous avez des données assez symétriques, ce problème ne sera pas présent, mais encore une fois, nous examinons le pire cas de jeux de données asymétriques
Ce que j'ai ensuite fait est de faire correspondre statistiquement les points médians ... ou du moins leurs couleurs. Donc, puisque notre valeur extrême (13) est dans la plage positive, nous laissons le jaune au 50e centile et essayons de la refléter dans la plage négative en modifiant le pourcentage de la couleur jaune qui apparaît (si la plage négative avait la valeur extrême, nous le ferions). laissez le jaune à ce 50e percentile et essayez de le refléter dans la plage positive). Cela signifie que, dans notre gamme négative, nous voulons déplacer notre jaune (50ème centile) de -2 à un nombre autour de -8.5 afin qu'il corresponde à la gamme positive. J'ai écrit une fonction appelée Function iGetPercentileFromNumber(my_range As Range, num_to_find As Double)
Cela ne fait que ça! Plus spécifiquement, il prend une plage et lit les valeurs dans un tableau. Il ajoute ensuite num_to_find
au tableau et détermine le pourcentage correspondant à num_to_find
en tant que i nteger 0-100 (d'où le i du nom de la fonction). En utilisant à nouveau nos exemples de données, nous appellerions quelque chose comme
imidcolorpercentile = iGetPercentileFromNumber(negrange with extra element -13, -8.5)
Où -8,5 est le négatif (50ème percentile de la plage positive = 8,5). Ne vous inquiétez pas, le code fournit automatiquement les plages et les chiffres, ceci est pour votre compréhension. La fonction ajouterait -8,5 à notre tableau de valeurs négatives {-13, -1, -1, -2, -2, -2, -3, -4, -8,5} puis déterminer quel centile c'est.
Maintenant, prenons ce centile et le transmettons comme point médian de notre formatage conditionnel de négation. donc nous avons changé le jaune du 50e centile
Selection.FormatConditions(1).ColorScaleCriteria(2).Value = 50
à notre nouvelle valeur
Selection.FormatConditions(1).ColorScaleCriteria(2).Value = imidcolorpercentile 'was 50
qui maintenant a décalé les couleurs !! nous avons essentiellement créé une barre de couleur d'aspect symétrique. Même si nos chiffres sont loin d'être symétriques.
Ok, je sais que c'était une tonne à lire et à digérer. mais voici les principales conclusions de ce code - utilise la mise en forme conditionnelle complète à 3 couleurs (ne pas simplement définir les deux couleurs extrêmes de la même manière pour ressembler à la valeur abs) - crée des plages de couleurs symétriques en utilisant un cellule obstruée (par exemple sous un bouton) pour conserver les valeurs extrêmes - utilise une analyse statistique pour faire correspondre les dégradés de couleurs, même dans des ensembles de données asymétriques
les deux étapes sont nécessaires et aucune ne suffit à elle seule pour créer une vraie échelle de couleurs en miroir
Étant donné que cette solution nécessite une analyse statistique de l'ensemble de données, vous devrez l'exécuter à chaque fois que vous modifiez un numéro (ce qui était en fait le cas auparavant, je ne l'ai jamais dit).
et maintenant le code. Mettez-le dans vba ou un autre programme de mise en évidence. Il est presque impossible de lire tel quel ..... prend une profonde respiration
Sub main()
Dim Rng As Range
Dim Cell_under_button As String
Set Rng = Range("A1:H10") 'change me!!!!!!!
Cell_under_button = "A15"
Call AbsoluteValColorBars(Rng, Cell_under_button)
End Sub
Function iGetPercentileFromNumber(my_range As Range, num_to_find As Double)
If (my_range.Count <= 0) Then
Exit Function
End If
Dim dval_arr() As Double
'this is one bigger than the range becasue we will add "num_to_find" to it
ReDim dval_arr(my_range.Count + 1)
Dim icurr_idx As Integer
Dim ipos_num As Integer
icurr_idx = 0
'creates array of all the numbers in your range
For Each cell In my_range
dval_arr(icurr_idx) = cell.Value
icurr_idx = icurr_idx + 1
Next
'adds the number we are searching for to the array
dval_arr(icurr_idx) = num_to_find
'sorts array in descending order
dval_arr = BubbleSrt(dval_arr, False)
'if match_type is 0, MATCH finds an exact match
ipos_exact = Application.Match(CLng(num_to_find), dval_arr, 0)
'there is a runtime error that can crop up when num_to_find isn't formated as long
'so we converted it, if it was a double we may not find an exact match so ipos_Exact
'may fail. now we have to find the closest numbers below or above clong(num_to_find)
'If match_type is -1, MATCH finds the value <= num_to_find
ipos_small = Application.Match(CLng(num_to_find), dval_arr, -1)
If (IsError(ipos_small)) Then
Exit Function
End If
'sorts array in ascending order
dval_arr = BubbleSrt(dval_arr, True)
'now we find the index of our mid color point
'If match_type is 1, MATCH finds the value >= num_to_find
ipos_large = Application.Match(CLng(num_to_find), dval_arr, 1)
If (IsError(ipos_large)) Then
Exit Function
End If
'barring any crazy errors descending order = reverse order (ascending) so
ipos_small = UBound(dval_arr) - ipos_small
'to minimize color error we pick the value closest to num_to_find
If Not (IsError(ipos_exact)) Then
'barring any crazy errors descending order = reverse order (ascending) so
'since the index was WRT descending subtract that from the length to get ascending
ipos_num = UBound(dval_arr) - ipos_exact
Else
If (Abs(dval_arr(ipos_large) - num_to_find) < Abs(dval_arr(ipos_small) - num_to_find)) Then
ipos_num = ipos_large
Else
ipos_num = ipos_small
End If
End If
'gets the percentile as an integer value 0-100
iGetPercentileFromNumber = Round(CDbl(ipos_num) / my_range.Count * 100)
End Function
'fairly well known algorithm doesn't need muxh explanation
Public Function BubbleSrt(ArrayIn, Ascending As Boolean)
Dim SrtTemp As Variant
Dim i As Long
Dim j As Long
If Ascending = True Then
For i = LBound(ArrayIn) To UBound(ArrayIn)
For j = i + 1 To UBound(ArrayIn)
If ArrayIn(i) > ArrayIn(j) Then
SrtTemp = ArrayIn(j)
ArrayIn(j) = ArrayIn(i)
ArrayIn(i) = SrtTemp
End If
Next j
Next i
Else
For i = LBound(ArrayIn) To UBound(ArrayIn)
For j = i + 1 To UBound(ArrayIn)
If ArrayIn(i) < ArrayIn(j) Then
SrtTemp = ArrayIn(j)
ArrayIn(j) = ArrayIn(i)
ArrayIn(i) = SrtTemp
End If
Next j
Next i
End If
BubbleSrt = ArrayIn
End Function
Sub AbsoluteValColorBars(Rng As Range, Cell_under_button As String)
negrange = ""
posrange = ""
'deletes existing rules
Rng.FormatConditions.Delete
'makes a negative and positive range
For Each cell In Rng
If cell.Value < 0 Then
' im certain there is a better way to get the column character
negrange = negrange & Chr(cell.Column + 64) & cell.Row & ","
Else
' im certain there is a better way to get the column character
posrange = posrange & Chr(cell.Column + 64) & cell.Row & ","
End If
Next cell
'removes trailing comma
If Len(negrange) > 0 Then
negrange = Left(negrange, Len(negrange) - 1)
End If
If Len(posrange) > 0 Then
posrange = Left(posrange, Len(posrange) - 1)
End If
'finds the data extrema
most_pos = WorksheetFunction.Max(Range(posrange))
most_neg = WorksheetFunction.Min(Range(negrange))
'initial values
neg_range_percentile = 50
pos_range_percentile = 50
'if the negative range has the most extreme value
If (most_pos + most_neg < 0) Then
'put the corresponding positive number in our obstructed cell
Range(Cell_under_button).Value = -1 * most_neg
'and add it to the positive range, to reskew the data
posrange = posrange & "," & Cell_under_button
'gets the 50th percentile number from neg range and tries to mirror it in pos range
'this should statistically skew the data
the_num = WorksheetFunction.Percentile_Inc(Range(negrange), 0.5)
pos_range_percentile = iGetPercentileFromNumber(Range(posrange), -1 * the_num)
Else
'put the corresponding negative number in our obstructed cell
Range(Cell_under_button).Value = -1 * most_pos
'and add it to the positive range, to reskew the data
negrange = negrange & "," & Cell_under_button
'gets the 50th percentile number from pos range and tries to mirror it in neg range
'this should statistically skew the data
the_num = WorksheetFunction.Percentile_Inc(Range(posrange), 0.5)
neg_range_percentile = iGetPercentileFromNumber(Range(negrange), -1 * the_num)
End If
'low red high green for positive range
Call addColorBar(posrange, False, pos_range_percentile)
'high red low green for negative range
Call addColorBar(negrange, True, neg_range_percentile)
End Sub
Sub addColorBar(my_range, binverted, imidcolorpercentile)
If (binverted) Then
'ai -> array ints
adcolor = Array(8109667, 8711167, 7039480)
' green , yellow , red
Else
adcolor = Array(7039480, 8711167, 8109667)
' red , yellow , greeb
End If
Range(my_range).Select
'these were just found using the record macro feature
Selection.FormatConditions.AddColorScale ColorScaleType:=3
Selection.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
'assigns a color for the lowest values in the range
Selection.FormatConditions(1).ColorScaleCriteria(1).Type = _
xlConditionValueLowestValue
With Selection.FormatConditions(1).ColorScaleCriteria(1).FormatColor
.Color = adcolor(0)
.TintAndShade = 0
End With
'assigns color to... midpoint of range
Selection.FormatConditions(1).ColorScaleCriteria(2).Type = _
xlConditionValuePercentile
Selection.FormatConditions(1).ColorScaleCriteria(2).Value = imidcolorpercentile 'originally 50
With Selection.FormatConditions(1).ColorScaleCriteria(2).FormatColor
.Color = adcolor(1)
.TintAndShade = 0
End With
'assigns colors to highest values in the range
Selection.FormatConditions(1).ColorScaleCriteria(3).Type = _
xlConditionValueHighestValue
With Selection.FormatConditions(1).ColorScaleCriteria(3).FormatColor
.Color = adcolor(2)
.TintAndShade = 0
End With
End Sub
Je vais beaucoup emprunter à la réponse de @barryleajo (cela ne me fera pas de mal si vous sélectionnez cette réponse). Comme indiqué dans cette réponse, l'ordre de la mise en forme conditionnelle est la clé, commencez par les valeurs absolues les plus petites et progressez vers le haut. La différence entre cette réponse et celle-ci est qu'il n'est pas nécessaire d'utiliser une instruction "et", car le PO semble indiquer que toutes les valeurs comprises dans une certaine plage de valeur absolue doivent recevoir le même format de couleur. Voici un petit exemple: