People Analytics: utilizando Machine Learning na criação de um Mapa de Competências

Yves M. Galvão
Livelo
6 min readFeb 26, 2023

--

É inegável que machine learning e Inteligência Artificial (IA) se tornaram indispensáveis no nosso dia a dia. Basta olhar para a quantidade de ferramentas que fazem uso cada vez mais de deep learning e de novas tecnologias para entregar resultados cada vez mais impressionantes.

Temos ferramentas funcionais nas mais diversas áreas, tais como, criação de imagens, geração de textos e, é claro que não podemos deixar de falar do queridinho do momento, o ChatGpt. Porém, diferente dos super modelos atuais que utilizam uma abordagem supervisionada e que necessitam de bilhões de parâmetros durante seu treinamento, existe também os modelos de aprendizagem não supervisionada.

Tais técnicas se adequam a uma infinidade de problemas e não precisam, necessariamente, de um poder computacional grande para seu treinamento e/ou utilização. Além disso, dependendo do problema a ser resolvido, uma solução não supervisionada pode nos atender melhor do que um modelo supervisionado. Algoritmos de detecção de anomalia, por exemplo, são usados amplamente para identificar padrões anômalos em fluxos de trabalho padrões. Já os algoritmos de clusterização também são amplamente utilizados para agrupar semelhantes em grupos comuns.

Aqui na Livelo, temos explorado oportunidades de uso desses algoritmos em diferentes cenários, como visto no artigo Maior velocidade e eficácia: automatizando Testes de Performance para otimizar resultados. Além disso, outro case recente foi a utilização de algoritmos de "clusterização" para classificar pessoas similares e agrupar seus skills. Mas antes de entrarmos na solução de fato, vamos abordar um pouco sobre o problema que foi explorado.

Contexto

Com um grande número de colaboradores distribuídos em diferentes tribos e squads, tornou-se cada vez mais difícil ter um mapa de competências que fosse uma fotografia de todas as nossas skills técnicas existentes, o que dificulta na distribuição correta de capacity de um time e de alocação de recursos de uma maneira mais efetiva. Também tínhamos a necessidade de montar o time ideal quando um novo projeto é iniciado e, muitas vezes, a escolha dos integrantes limitava-se a indicações internas. Esses cenários estão ilustrados a seguir:

#Cenário 1

Mapear times e skills para que tenhamos um mapa de competências, além de conseguir inferir a média do time, identificando possíveis gaps e realizando processo de mentoria para evoluir essas habilidade.

Exemplo de mapa de competência preenchido por um time.

#Cenário 2

Encontrar perfis similares para que seja possível realizar job rotation ou manter um time operacional com uma saída inesperada.

Exemplo de utilização da pesquisa de similares do mapa de competência.

#Cenário 3

Dado um conjunto de habilidades necessárias para realização de um projeto, encontrar pessoas que mais se aproximem dessa lista.

Exemplo de utilização do mapa de competências para estruturar um time ou uma frente de trabalho.

Solução

Baseado nessas informações e levando em conta o conceito de Produto Mínimo Viável (MVP), iniciamos o desenvolvimento da nossa primeira Prova de Conceito (POC), no qual escolhemos uma squad candidata e utilizamos uma planilha simples que continha uma lista de skills do time.

Lista de pessoas x skills.

Cada integrante do time foi, então, convidado para preencher com uma nota de 0 a 5 seu nível naquela lista de skills.

Com essas informações podemos mapear a média do time para cada tecnologia e se tínhamos alguma pessoa de referência em uma determinada tecnologia (Nota igual ou superior a 4), além de comparar diferentes níveis de skills e permitir o desenvolvimento de um Plano de Desenvolvimento Integrado (PDI) para cada integrante.

Nós observamos que a ideia da ferramenta poderia ser estendida para outros times e decidimos expandir a coleta dos dados. Porém, surgiu também um novo questionamento porque a consulta de dados não seria simples com os dados distribuídos entre diferentes squads em uma planilha.

Com isso, partimos para o desenvolvimento de uma segunda POC, dessa vez mais elaborada e que deveria fazer uso de machine learning para permitir uma pesquisa específica de pessoas para um dado conjunto de skills. Também era necessário que essa POC permitisse agrupar pessoas similares. Para isso, decidimos utilizar Python para o desenvolvimento e o Google Collab.

O primeiro passo foi definir o conjunto de bibliotecas que seriam utilizados:

import os
import pandas as pd
import glob
from sklearn.neighbors import NearestNeighbors
from sklearn.neighbors import kneighbors_graph
import numpy as np
import matplotlib.pyplot as plt

Na sequência, utilizamos um conjunto de skills para realização dos testes: C, C++, C#, Java e Python, distribuídas em um arquivo Comma-separated values (CSV) com o seguinte formato:

Names,C,C++,C#,JAVA,PYTHON
Luke,3,0,0,5,5
Vadder,5,0,0,5,5
Obi-wan,5,2,3,3,0
Han-solo,5,2,0,2,2
R2D2,5,5,0,0,0
Leia,2,2,2,3,3
Yoda,4,5,5,5,5

Com este conjunto de dados, utilizamos o Pandas para exibir as informações em uma tabela e verificar os dados de cada membro, similar ao resultado alcançado anteriormente utilizando uma planilha:

file = max(glob.glob("/content/Tech*.csv"), key=os.path.getmtime)
df = pd.DataFrame(pd.read_csv(file))
df
Lista de pessoas x skills.

Com essas informações já foi possível inferir os dados básicos da primeira POC (média, referência, gráfico do time e ranking para uma determinada linguagem).

df.describe()
Lista com as informações de média, desvio padrão, valores mínimos e máximos para cada skill.

Também se tornou fácil verificar o ranking de uma determinada skill:

tech = "C#"
df['rank'] = df[tech].rank()
df = df.sort_values(by='rank', ascending=False)
df
Lista ordenada de pessoas que possuem a maior nota para C#.

E ter essas informações exibidas em um gráfico:

df = pd.DataFrame(pd.read_csv(file))
df_mean = df.mean().round(2)

ax = df_mean.plot.bar(rot=60, title="Média do time")
ax.set_ylim(0,5)
Um gráfico de barras com o eixo X com os valores C, C++, C#, Java e Python e eixo Y com os valores, 4, 2.1, 1.5, 3.2 e 2.9.
Gráfico com a média de um time para um conjunto de skills.

De maneira similar, também conseguimos verificar o radar para a média e valores de referência do time:

def person_radar_chart(person, max=5,person_name='Média do time'):
angles=np.linspace(0, 2*np.pi, person.shape[0], endpoint=False)

stats=np.concatenate((person,[person[0]]))
angles=np.concatenate((angles,[angles[0]]))
labels = np.concatenate((df_mean.index.values.tolist(), [df_mean.index.values.tolist()[0]]))

fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
ax.plot(angles, stats, 'o-', linewidth=2)
ax.fill(angles, stats, alpha=0.25)
ax.set_thetagrids(angles * 180/np.pi, labels)
ax.set_title(person_name)
ax.set_rlim(0,max)
ax.grid(True)
#Exibe o gráfico do radar com a média de um time
person_radar_chart(df_mean)
Um gráfico em forma de radar exibindo um conjunto de habilidades e seus respectivos valores.
Gráfico de radar para as skills médias do time.
df = pd.DataFrame(pd.read_csv(file))
df = df.drop('Names', 1)
#separa apenas os valores máximos de cada coluna
df_max = df.max().to_frame()

#Exibe o gráfico do radar com o valor mais alto de cada skill do time
person_radar_chart(df_max[0])
Gráfico de radar para as skills de referência do time.

Seguimos então para o processo de "clusterização" e de agrupamento de similares. O algoritmo escolhido foi o k-nearest neighbors (k-NN) com o número de "k" definido de sete, que corresponde ao número de similares que desejávamos encontrar.

#Retirando os nomes do dataset
arrayWithoutNames = df.iloc[: , 1:]
arrayWithoutNames = arrayWithoutNames.to_numpy()

#k-nn
knn = NearestNeighbors(n_neighbors=df.shape[0])
knn.fit(arrayWithoutNames)

Feito a "clusterização" baseado na quantidade de "k" que desejávamos, agora precisávamos apenas passar as skills que estávamos procurando e — voilà — uma lista de pessoas nos é apresentada de acordo com as informações que solicitamos.

#Pesquisando por pessoas mais próximas a C, Java e Python com nota 5 
#Atribuindo peso -10 nas demais skills que não estão sendo utilizadas
targetSkills = [5, -10, -10, 5, 5]
result = knn.kneighbors([targetSkills])
df_result = df.iloc[result[1][0]].set_index('Names')
df_result
Lista de similares que possuem skills mais apuradas em C, Java e Python.

De forma parecida, nós podemos passar o perfil de uma pessoa e procurar outras similares, atendendo, assim, todos os cenários que foram propostos no início desse artigo.

user_name = "Vadder"
df_user = df.loc[df['Names'] == user_name]
arrayWithoutNameByUserName = df_user.iloc[: , 1:].to_numpy()

df_without_user = df.loc[df['Names'] != user_name]
df_without_column_name_and_current_user = df_without_user.iloc[: , 1:].to_numpy()
knn = NearestNeighbors()
knn.fit(df_without_column_name_and_current_user)
result = knn.kneighbors(arrayWithoutNameByUserName)

df_without_user.iloc[result[1][0]]
Lista de pessoas mais similares a Vadder.

Conclusão

Embora o conceito de machine learning já esteja amplamente difundido, os diferentes tipos de aprendizagem, algoritmos e técnicas tornam as suas possibilidades de uso praticamente ilimitadas. Como visto durante o desenvolvimento deste artigo, muito além de utilizar a última tecnologia disponível, saber utilizar a melhor abordagem para cada cenário pode trazer resultados simples, rápidos e precisos com um tempo de desenvolvimento menor e sem a necessidade de um custo computacional elevado.

--

--