Friday, October 23, 2015

What Does the AVERAGE Brand Logo Look Like?


PNG images are essentially a grid of values that represent colors to display. Since each cell in the grid is made up of numbers, I got curious about what it might mean to aggregate multiple PNGs. What would it look like to average two or more images? Median? Mode? Random?

To do so, I pulled the top 100 brands' logos from Best Global Brands.
Then I used the (layers of) values as inputs to aggregate in various ways.

Averaging these logos yields this gray blob that looks roughly, well, saturnine.

 Taking the median value results in what looks like a messy paintbrush stroke.

The mode reflects the heavy use of black.
The random one looks galactic! I like it the most...


Clearly, there is quite the uniformity in the logo design. Both horizontal and vertical symmetry are present. There is a bias towards a wider shape, similar to the dimensions of a word. Also, three general shapes tend to appear:  a perfect square, a perfect circle, and the long rectangle.

Is there one agency that designed most of these? They have so much in common.

Below is the R code. It is long because it reflects the evolution of my thought process. A dash of apply could speed up the explicit naming.


1:    
2:  # Prepare -----------------------------------------------------------------  
3:  rm(list=ls());gc()  
4:  pkg <- c("RCurl","XML","png","data.table","reshape","grid")  
5:  inst <- pkg %in% installed.packages()  
6:  if(length(pkg[!inst]) > 0) install.packages(pkg[!inst])  
7:  lapply(pkg,library,character.only=TRUE)  
8:  rm(inst,pkg)  
9:  setwd("your folder here")  
10:  set.seed(4444)  
11:    
12:    
13:  # Download HTML -----------------------------------------------------------  
14:  doc <- htmlParse("http://interbrand.com/best-brands/best-global-brands/2015/ranking/",  
15:           encoding="UTF-8")  
16:    
17:    
18:  # Parse HTML for image sources and info ----------------------------------  
19:  plain.src <- xpathApply(doc,"//img[@class='logo-img']",xmlGetAttr,"src")  
20:  plain.alt <- xpathApply(doc,"//img[@class='logo-img']",xmlGetAttr,"alt")  
21:    
22:  plain.rank <- xpathApply(doc,"//div[@class='brand-info brand-rank brand-col-1']",  
23:               xmlGetAttr,"title")  
24:  plain.region <- xpathApply(doc,"//div[@class='brand-info brand-region']",  
25:               xmlGetAttr,"title")  
26:  plain.country <- xpathApply(doc,"//div[@class='brand-info brand-country brand-col-5']",  
27:                xmlGetAttr,"title")  
28:  plain.sector <- xpathApply(doc,"//div[@class='brand-info brand-sector brand-col-6']",  
29:                xmlGetAttr,"title")  
30:  plain.value <- xpathApply(doc,"//div[@class='brand-info brand-value brand-col-7']",  
31:                xmlGetAttr,"title")  
32:    
33:    
34:  # Compile info ------------------------------------------------------------  
35:  d0 <- data.frame(Rank=unlist(plain.rank),  
36:           Country=unlist(plain.country),  
37:           Region=unlist(plain.region),  
38:           Sector=unlist(plain.sector),  
39:           Value=unlist(plain.value),  
40:           stringsAsFactors=FALSE)  
41:    
42:  d0$Rank <- gsub("Rank: ","",d0$Rank)  
43:  d0$Rank <- as.numeric(d0$Rank)  
44:  d0$Country <- gsub("Country: ","",d0$Country)  
45:  d0$Region <- gsub("Region: ","",d0$Region)  
46:  d0$Sector <- gsub("Sector: ","",d0$Sector)  
47:  d0$Value <- gsub("Value: ","",d0$Value)  
48:  d0$Value <- gsub("[^0-9]","",d0$Value)  
49:  d0$Value <- as.numeric(d0$Value)*1000000  
50:    
51:    
52:  # Download images ---------------------------------------------------------  
53:  n <- length(plain.src)  
54:  for(i in 1:n) {  
55:   if(!file.exists(paste0("Rank",d0$Rank[i],".png"))) {  
56:    download.file(paste0("http://interbrand.com",plain.src[[i]]),  
57:           destfile=paste0("Rank",d0$Rank[i],".png"),mode="wb")  
58:    Sys.sleep(0.1)  
59:   }  
60:  }  
61:    
62:    
63:  # Read in logo PNGs -------------------------------------------------------  
64:  for(i in 1:n) {  
65:   assign(paste0("Rank",d0$Rank[i]),readPNG(paste0("Rank",d0$Rank[i],".png")))  
66:  }  
67:  dims <- dim(Rank1)  
68:    
69:    
70:  # Combine arrays ----------------------------------------------------------  
71:  d1 <- vector()  
72:  for(i in 1:n) {  
73:   d1 <- c(d1,as.vector(get(ls()[grep(pattern="Rank.*",x=ls())][i])))  
74:  }  
-75:  a1 <- array(d1,c(dims,n))  
76:    
77:    
78:  # Clean up environment ----------------------------------------------------  
79:  rm(list=ls()[!ls() %in% c("d1","a1","n","dims")])  
80:    
81:    
82:  # Get element-wise metrics -----------------------------------------------  
83:  logo1.avg <- apply(a1,1:3,mean)  
84:  logo1.med <- apply(a1,1:3,median)  
85:  logo1.mod <- apply(a1,1:3,function(x) unique(x)[which.max(tabulate(match(x,unique(x))))])  
86:  logo1.ran <- apply(a1,1:3,sample,1)  
87:    
88:    
89:  # Display results ---------------------------------------------------------  
90:  grid.raster(logo1.avg)  
91:  dev.off()  
92:  grid.raster(logo1.med)  
93:  dev.off()  
94:  grid.raster(logo1.mod)  
95:  dev.off()  
96:  grid.raster(logo1.ran)  
97:  dev.off()  
98:    
99:    

5 comments:

  1. Hi, this is awesome!

    When I tried your code, it gives the following error message. Could you help me out by pointing what'g wrong?

    Error in plain.src[[i]] : subscript out of bounds

    Thanks in advance!!!

    ReplyDelete
  2. Seems the page was permanently moved, I'm trying it with this link: http://interbrand.com/best-brands/best-global-brands/2015/ranking/

    You will also have to adjust line 63 to download.file(paste0("http://interbrand.com",plain.src[[i]]),destfile=paste0(d1$Brand[i],".png"),mode="wb") . And it probably works better not referencing Brand names, but filenames from the assets.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. I have updated the code to make it work with the source website that was moved after the original post.

    ReplyDelete