O objetivo aqui é tentar reproduzir os resultados deste post (do blog o pequeno investidor) que avalia alguns indicadores da Petrobrás no cenário atual, começo de 2014, utilizando Python e alguns pacotes do stack científico.
Vamos começar importando os dados e para isso vamos utilizar o pandas.
pandas possui uma classe denominada DataFrame
que pode carregar dados em formatos híbridos em uma estrutura tabular.
Isso é muito legal porque em muitos casos os dados não estão em formatos numéricos e frequentemente é necessário, mesmo em dados numéricos, que eles sejam categorizados e ao invés de utilizar códigos sem sentido podemos lançar mão de identificadores de texto.
Mas felizmente os dados que vamos começar a análisar são numéricos e representam o a Demostração de Resultados da Petrobrás.
Estes dados foram obtidos do site Fundamentus que organiza as informações históricas de Balanço e Demostração de Resultados para diversas empresas, de graça!
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import sys
%pylab inline
print 'python', sys.version
print 'pandas', pd.__version__
Importando os dados¶
Vamos começar a análise observando os dados de resultados operacionais da Petrobrás: Receita Líquida e Lucro Líquido. Estas variáveis se encontram no Demonstrativo de Resultados da empresa, e estão no arquivo PETR balanco - T Dem. Result.csv
. Vamos começar dando uma olhada nesse arquivo.
!head -3 'PETR balanco - T Dem. Result.csv'
Bem, já dá pra ver que os nomes das colunas não são religiosos o que já implica no esforço de renomear algumas colunas para tornar a manipulação mais simples. Outra coisa é o formato de data, que no Brasil é dia/mês/ano. Para o pandas isso não é um problema como veremos, com algumas opções vamos importar o arquivo sem maiores problemas.
dem_result = pd.read_csv('PETR balanco - T Dem. Result.csv', parse_dates=[0], dayfirst=True)
print 'shape:', dem_result.shape
print 'columns:'
print dem_result.dtypes
Como podemos observar o DataFrame
dem_result
possui 32 linhas e 25 colunas, das quais: 1 datetime64
, 21 float64
, e 3 int64
.
Os campos numéricos foram identificados na carga do DataFrame
, já o campo data foi identificado utilizando as opções parse_dates=[0]
e dayfirst=True
indicando que a primeira coluna possui uma data e que na data o primeiro campo era o dia.
dem_result.Data.head()
O interessante é que a função read_csv
já resolve uma parte importante do meu problema que é a transformação dos dados. Para o que eu pretendo fazer com este dataset todas as colunas já estão com os tipos corretamente definidos.
Arrumando os nomes das colunas¶
Agora vamos atacar as colunas! Algumas colunas estão com os nomes zoados, com espaços, acentos e outros símbolos que dificultam a utilização do DataFrame
, por isso vamos renomear as colunas que desejamos utilizar. O DataFrame
possui um método rename
que torna essa tarefa fácil-fácil.
dem_result = dem_result.rename(columns={'Receita Líquida de Vendas e/ou Serviços':'ReceitaLiquida',
'Lucro/Prejuízo do Período':'LucroLiquido'})
Do ponto de vista do acionista a Petrobrás está estagnada¶
O artigo argumenta que do ponto de vista do acionista a empresa está estagnada desde 2004.
Bem, para tentarmos observar isso vamos criar outro dataset com as colunas Data
, ReceitaLiquida
, e LucroLiquido
, que são variáveis objeto da nossa análise.
oper_result = dem_result.loc[:,['Data', 'ReceitaLiquida', 'LucroLiquido']]
oper_result.head()
Vamos agora visualizar a evolução dessas grandezas ao longo dos anos e entender um pouco melhor a dinâmica delas.
import datetime
import matplotlib.dates as mdates
from matplotlib.ticker import FuncFormatter
years = mdates.YearLocator() # every year
months = mdates.MonthLocator() # every month
yearsFmt = mdates.DateFormatter('%Y')
fig, ax = plt.subplots(figsize=(10,6))
xx = [datetime.date(d.year, d.month, d.day) for d in oper_result.Data]
rects1 = ax.plot(xx, oper_result.LucroLiquido, 'go-')
rects2 = ax.plot(xx, oper_result.ReceitaLiquida, 'bo-')
ax.xaxis.set_major_locator(years)
ax.xaxis.set_major_formatter(yearsFmt)
ax.xaxis.set_minor_locator(months)
datemin = datetime.date(oper_result.Data.min().year, 1, 1)
datemax = datetime.date(oper_result.Data.max().year+1, 1, 1)
ax.set_xlim(datemin, datemax)
formatter = FuncFormatter(lambda x, pos: '$%1.1fB' % (x*1e-6))
ax.yaxis.set_major_formatter(formatter)
ax.set_title('Receita e Lucro trimestrais')
ax.legend( (rects1[0], rects2[0]), ('LucroLiquido', 'ReceitaLiquida'), loc='center right')
ax.grid(True)
O gráfico acima, que by the way me deu muito trabalho para criar exatamente da forma que eu queria (talvez no futuro eu escreva um post sobre isso), apresenta o resultado operacional da Petrobrás. Uma comparação da sua receita líquida, ao longo de aproximadamente 8 anos, contra o seu lucro líquido. É bastante claro que há uma diferença significativa entre o Lucro e a Receita. Esta última cresceu significativamente nos últimos anos, por outro lado o Lucro não acompanhou essa toada.
No entanto, eu gostaria de ver estes números consolidados por ano e avaliar esta evolução em um gráfico de barras.
Para isso vamos criar uma coluna Ano
no DataFrame
e fazer o agrupamento por ela.
oper_result['Ano'] = [d.year for d in oper_result.Data]
anual_result = oper_result.groupby(['Ano'])[['ReceitaLiquida', 'LucroLiquido']].sum()
fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(10,10))
rects1 = ax[0].plot(anual_result.index, anual_result.LucroLiquido, 'go-')
rects2 = ax[0].plot(anual_result.index, anual_result.ReceitaLiquida, 'bo-')
ax[0].set_xticks(anual_result.index + 0.35)
ax[0].set_xticklabels( anual_result.index )
ax[0].grid(True)
ax[0].set_title('Receita e Lucro anuais')
ax[0].legend( (rects1[0], rects2[0]), ('LucroLiquido', 'ReceitaLiquida'), loc='upper left')
ax[0].yaxis.set_major_formatter(formatter)
rects1 = ax[1].bar(anual_result.index, anual_result.LucroLiquido, 0.35, color='g')
rects2 = ax[1].bar(anual_result.index+0.35, anual_result.ReceitaLiquida, 0.35, color='b')
ax[1].set_title('Receita e Lucro anuais')
ax[1].set_xticks(anual_result.index + 0.35)
ax[1].set_xticklabels( anual_result.index )
ax[1].legend( (rects1[0], rects2[0]), ('LucroLiquido', 'ReceitaLiquida'), loc='upper left')
ax[1].grid(True)
ax[1].yaxis.set_major_formatter(formatter)
Tarefa relativamente fácil com pandas, agrupar o DataFrame
por uma coluna e definir qual função deve ser utilizada no agrupamento. No entanto, para mim seria mais intuitivo utilizar uma abordagem mais funcional para o agrupamento.
Poderiamos ter algo como
oper_ressult.groupby(['Ano'], ['ReceitaLiquida', 'LucroLiquido'], sum)
Eventualmente poderiamos definir transformações diferentes para o agrupamento de diferentes colunas. De qualquer forma, o problema está resolvido e foi relativamente fácil.
O gráfico em barras acima é melhor do que o gráfico em linhas quando queremos tornar mais evidente que as quantidades, Lucro e Receita, são bastante diferentes, no que se refere a massa. É notório que o Lucro é muito menor do que a receita. O gráfico em barras traz a evolução temporal e enfatiza a ordem de grandeza das duas variáveis. O gráfico de linha dá mais foco a dinâmica e pode, eventualmente, negligenciar a ordem de grandeza, por outro lado torna mais evidente tendências nas variáveis.
Bem, essas variáveis não são difíceis de entender. Receita é o que a empresa fatura e lucro é o que ela põe no bolso. Não tá muito difícil ver que a distância entre o que a Petrobrás ganha e o que ela põe no bolso vem aumentando significativamente e não tem mágica, esse dinheiro tem que ir pra algum lugar. Então em termos de lucro a Petrobrás realmente deu uma estagnada nos últimos anos.
Mas apenas o lucro e a receita não definem essa questão. Outra variável importante e complementar ao que estamos observando é o lucro pro ação. Para calcular o lucro por ação basta dividir o lucro líquido pela quantidade total de ações da empresa. Essa informação pode ser obtida no site da BM&F Bovespa, em 2013 a Petrobrás tinhas 6.743.719.951 ações (somando Preferenciais e Ordinárias).
anual_result.Qtd = 6743719951
anual_result.LPA = anual_result.LucroLiquido*1000/anual_result.Qtd
fig, ax = plt.subplots(figsize=(10,6))
ax.plot(anual_result.index, anual_result.LPA, 'go-')
ax.set_xticks(anual_result.index)
ax.set_xticklabels(anual_result.index)
ax.grid(True)
ax.set_title('LPA')
LPA significa lucro por ação e indica, em R$, quanto a empresa paga ao acionista por deter a sua ação. Como opequenoinvestidor menciona, o número por si não diz muita coisa, no entanto, se este número é crescente ao longo do tempo e a quantidade de ações da empresa permanece constante, significa que a empresa está lucrando e que sua ação está sofrendo os bons reflexos da sua performance e portanto o acionista está sendo recompensado.
No entanto, a Petrobrás aumentou o número de ações em 2010, ou seja, a empresa fez uma captação. Por isso o aumento da receita.
O gráfico acima não reflete os resultados apresentados no post do opequenoinvestidor e nem os números apresentados no site do Bastter. Acredito que isto aconteca porque não tenho a série da quantidade de ações por ano. No site da BM&F Bovespa tem apenas a quantidade de ações em 29/04/2013. Dado que houve a capitalização em 2010 a quantidade de ações antes de 2010 deve ser menor da que eu utilizei. Devemos então considerar esta série de LPA como uma proxy.
Entretanto é possível observar que o LPA vem decrescendo desde 2010, reflexo da captalização e da ausência de lucros expressivos, para compensar este aumento.
import Quandl
petr4 = Quandl.get("GOOG/BVMF_PETR4", authtoken='nJ1NhTYdEs2p3MsS4CVd')
petr4 = pd.DataFrame({'Fechamento' : list(petr4['Close']), 'Data' : list(petr4.index)}, index=np.arange(len(petr4)))
petr4.head()
Capturamos a série temporal das ações preferenciais da Petrobrás (PETR4) e para efeito de comparação vamos pegar o último ponto de cada ano para formar uma série temporal anual e comparar com os dados de receita e lucro.
petr4['Ano'] = [d.year for d in petr4.Data]
g = petr4.groupby(['Ano'])
anual_petr4 = g.apply(lambda x: x.sort('Data').loc[:,['Ano', 'Fechamento']].tail(1))
anual_petr4.index = anual_petr4.Ano
anual_petr4 = anual_petr4.loc[:,'Fechamento']
anual_petr4.tail()
Tendo a série anual, temos agora que fazer um join
dos DataFrame
.
anual_result_2 = anual_result.join(anual_petr4, how='outer')
anual_result_2
O join
é legal porque ele atua sobre o index
das estruturas de dado, e ai pode ser DataFrame
ou Series
, mas apenas DataFrame
possui o método join
. Portanto, Podemos apenas fazer o join
entre DataFrame
e de uma Series
em um DataFrame
, como vimos acima.
Utilizei o parâmetro how=outer
para obter a maior abrangência dos índices e consequentemente as colunas ReceitaLiquida
e LucroLiquido
vieram com valores NaN
, pois não possuem dados referentes aos anos de 2003, 2004 e 2014.
fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(10,10), dpi=300)
rects1 = ax[0].bar(anual_result_2.index, anual_result_2.LucroLiquido, 0.35, color='g')
rects2 = ax[0].bar(anual_result_2.index+0.35, anual_result_2.ReceitaLiquida, 0.35, color='b')
ax[0].set_title('Receita e Lucro anuais')
ax[0].set_xticks(anual_result_2.index + 0.35)
ax[0].set_xticklabels(anual_result_2.index)
ax[0].legend( (rects1[0], rects2[0]), ('LucroLiquido', 'ReceitaLiquida'), loc='upper left')
ax[0].grid(True)
ax[1].plot(anual_result_2.index, anual_result_2.Fechamento, 'o-')
ax[1].set_xticks(anual_result_2.index)
ax[1].set_xlim((2003,2014))
ax[1].set_title('PETR4')
ax[1].grid(True)
É importante notar que as colunas com NaN
não aparecem no gráfico, ou seja, eu possuo um registro de índice mas não possuo ponto para o registro. No gráfico em barras não fica estranho, no entanto, no gráfico em linha ficariam segmentos soltos no meio do gráfico.
Conclusão¶
Por ora, podemos observar com os dados:
- a discrepância entre receita e lucro
- a queda do lucro por ação
Ainda coloquei o gráfico de preços das ações junto dos resultados operacionais para avaliar como a variação nos preços estaria relacionada com estas variáveis. É possível observar que assim como o LPA o valor da ação vem diminuindo desde 2010, o que não é um bom indicador para os acionistas.
Vamos ficando por aqui mas a análise não terminou. Ainda temos outras variáveis para e o que foi colocado aqui é apenas o começinho do post.
Na verdade o grosso do trabalho foi de importação, formatação e compreensão dos dados e como utilizar o pandas para tudo isso. Isso consome tempo mesmo. Nos próximos textos vamos direto ao ponto já trabalhando nas variáveis e esmiuçando os indicadores da empresa utilizando Python.