In [1]:
import epmwebapi as epm
import datetime as dt
import numpy as np
import pandas as pd

Para não expor o usuário e senha do EPM, salvo essas informações numa variável de ambiente e busco via código

In [ ]:
import os
credentials = os.environ.get('user_and_password').split(';')
user = credentials[0]
password = credentials[1]

Leitura de dados brutos(Raw)

In [3]:
#cria conexao
epmConn = epm.EpmConnection('http://localhost:44333', 'http://localhost:44332', user, password)

path = 'R80711_Wind_speed'
bv = epmConn.getDataObjects(path)

iniTime = dt.datetime(2016, 1, 1, 0, 0, 0, 0)
endTime = iniTime + dt.timedelta(days=3)

#cria objeto queryperiod
queryPeriod = epm.QueryPeriod(iniTime, endTime)

#aplica o objeto um historyReadRaw com o queryPeriod na basicVariable selecionada
result = bv[path].historyReadRaw(queryPeriod)

#fechando a conexão
connection.close()

Mostra o formato dos dados

In [4]:
result.shape
Out[4]:
(432,)

Criação do dataframe pandas

corrige o problema: ValueError: Big-endian buffer not supported on little-endian compiler para ser possível transformar os dados vindos do EPM em um Dataframe Pandas

In [5]:
new_Quality = result[:]['Quality'].byteswap().newbyteorder()
new_Timestamp = result[:]['Timestamp']
new_Value = result[:]['Value'].byteswap().newbyteorder()

d = {'Value':new_Value, 'Timestamp':new_Timestamp, 'Quality':new_Quality}
df_original = pd.DataFrame(d)

Mostra o formato dos dados, para esse caso são 432 linhas por 3 colunas

In [6]:
df_original.shape
Out[6]:
(432, 3)

Comparação entre valores em formato Numpy Array e Dataframe do Pandas

In [8]:
print("Numpy Array:")
print(result[0:5])
print("\n")
print("Dataframe Pandas:")
df_original.head()
Numpy Array:
[(6.14, datetime.datetime(2016, 1, 1, 0, 0, tzinfo=datetime.timezone.utc), 0)
 (5.75, datetime.datetime(2016, 1, 1, 0, 10, tzinfo=datetime.timezone.utc), 0)
 (5.71, datetime.datetime(2016, 1, 1, 0, 20, tzinfo=datetime.timezone.utc), 0)
 (6.07, datetime.datetime(2016, 1, 1, 0, 30, tzinfo=datetime.timezone.utc), 0)
 (5.75, datetime.datetime(2016, 1, 1, 0, 40, tzinfo=datetime.timezone.utc), 0)]


Dataframe Pandas:
Out[8]:
Value Timestamp Quality
0 6.14 2016-01-01 00:00:00+00:00 0
1 5.75 2016-01-01 00:10:00+00:00 0
2 5.71 2016-01-01 00:20:00+00:00 0
3 6.07 2016-01-01 00:30:00+00:00 0
4 5.75 2016-01-01 00:40:00+00:00 0

Mostra o tipo dos dados em cada coluna

In [7]:
df_original.dtypes
Out[7]:
Value                    float32
Timestamp    datetime64[ns, UTC]
Quality                    int64
dtype: object

Gera estatísticas descritivas que resumem a tendência central, a dispersão e a forma da distribuição de um conjunto de dados, excluindo os valores NaN.

Aplicado somente a dados numéricos

In [10]:
df_original.describe()
Out[10]:
Value Quality
count 432.000000 432.0
mean 6.456759 0.0
std 1.637152 0.0
min 0.870000 0.0
25% 5.687500 0.0
50% 6.490000 0.0
75% 7.270000 0.0
max 11.180000 0.0

Inclui todos os tipos de dados

In [9]:
df_original.describe(include='all')
Out[9]:
Value Timestamp Quality
count 432.000000 432 432.0
unique NaN 432 NaN
top NaN 2016-01-01 20:40:00+00:00 NaN
freq NaN 1 NaN
mean 6.456759 NaN 0.0
std 1.637152 NaN 0.0
min 0.870000 NaN 0.0
25% 5.687500 NaN 0.0
50% 6.490000 NaN 0.0
75% 7.270000 NaN 0.0
max 11.180000 NaN 0.0

Somente os que são do tipo float32

In [11]:
df_original.describe(include=['float32'])
Out[11]:
Value
count 432.000000
mean 6.456759
std 1.637152
min 0.870000
25% 5.687500
50% 6.490000
75% 7.270000
max 11.180000

REMOVENDO COLUNAS

Boa prática ao realizar algumas alteração no dataframe é atribuir a outra variável

axis= 1 - Colunas / axis = 0 - Linhas

In [12]:
df_sem_timestamp = df_original.drop(['Timestamp'],axis=1)

df_sem_timestamp.describe(include='all')
Out[12]:
Value Quality
count 432.000000 432.0
mean 6.456759 0.0
std 1.637152 0.0
min 0.870000 0.0
25% 5.687500 0.0
50% 6.490000 0.0
75% 7.270000 0.0
max 11.180000 0.0

REMOVENDO LINHAS

In [ ]:
#axis= 1 - Colunas / axis = 0 - Linhas
df_sem_timestamp_v1 = df_sem_timestamp.drop([0,1],axis=0)

df_sem_timestamp_v1.head()

ITERAÇÕES

In [ ]:
for indice, linha in df_original.iterrows():
    print(indice, linha.Value , ' = ', linha.Timestamp)
    if indice == 5:
        break

ORDENAÇÃO

Ordenando com dot notation retorna uma serie

In [15]:
a = df_original.Value.sort_values(ascending=False)
type(a)
Out[15]:
pandas.core.series.Series
In [13]:
#Para ordernar direto o DF aplicar sort_values no DF
b = df_original.sort_values(['Value'], ascending=False)
type(b)
Out[13]:
pandas.core.frame.DataFrame

Filtros Simples

Usar a notação de colchetes para indexar ou para aplicar filtros de series booleanos

In [ ]:
booleanos = []
for item in df_original.Value:
    if item > 10:
        booleanos.append(True)
    else:
        booleanos.append(False)
        
#mostra se os primeiros 10 valores possuem "value" mais que 10
booleanos[:10]

Mostra que existem 13 dados com value maior que 30

In [17]:
df_filtrado = df_original[booleanos]
df_filtrado.shape
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-17-2879ed279fc6> in <module>
----> 1 df_filtrado = df_original[booleanos]
      2 df_filtrado.shape

NameError: name 'booleanos' is not defined

Simplificando ou utilizando em List Comprehension

In [16]:
# A é uma serie do pandas
A = df_original.Value > 10

#apresenta todos os dados maiores que 10
df_original[A]
Out[16]:
Value Timestamp Quality
393 10.33 2016-01-03 17:30:00+00:00 0
394 11.18 2016-01-03 17:40:00+00:00 0
395 11.14 2016-01-03 17:50:00+00:00 0
397 10.40 2016-01-03 18:10:00+00:00 0
398 10.76 2016-01-03 18:20:00+00:00 0
403 10.62 2016-01-03 19:10:00+00:00 0
404 10.36 2016-01-03 19:20:00+00:00 0
409 10.09 2016-01-03 20:10:00+00:00 0
410 10.32 2016-01-03 20:20:00+00:00 0
411 10.13 2016-01-03 20:30:00+00:00 0
412 10.26 2016-01-03 20:40:00+00:00 0
413 10.49 2016-01-03 20:50:00+00:00 0
419 10.21 2016-01-03 21:50:00+00:00 0

List Comprehension

In [ ]:
df_original[df_original.Value > 10].head()

Retorna somente a serie desejada

In [14]:
serie_timestamp = df_original[df_original.Value > 10].Timestamp
serie_timestamp.head()
Out[14]:
393   2016-01-03 17:30:00+00:00
394   2016-01-03 17:40:00+00:00
395   2016-01-03 17:50:00+00:00
397   2016-01-03 18:10:00+00:00
398   2016-01-03 18:20:00+00:00
Name: Timestamp, dtype: datetime64[ns, UTC]

Contudo, por boa prática, utilizarmos sempre o método LOC, que na verdade é definir uma condição e filtrar por labels

In [ ]:
#retorna dataframe
df_filtrado = df_original.loc[df_original.Value > 10]
df_filtrado.head()
In [ ]:
#retorna uma serie
serie_filtrada = df_original.loc[df_original.Value > 10, 'Timestamp']
serie_filtrada.head()

Filtros Multiplos

Operador lógico E

In [18]:
df_original[(df_original.Value > 10) & (df_original.Quality == 0)].head()
Out[18]:
Value Timestamp Quality
393 10.33 2016-01-03 17:30:00+00:00 0
394 11.18 2016-01-03 17:40:00+00:00 0
395 11.14 2016-01-03 17:50:00+00:00 0
397 10.40 2016-01-03 18:10:00+00:00 0
398 10.76 2016-01-03 18:20:00+00:00 0

Operador lógico OU

In [19]:
df_original[(df_original.Value > 10) | (df_original.Quality == 0)].head()
Out[19]:
Value Timestamp Quality
0 6.14 2016-01-01 00:00:00+00:00 0
1 5.75 2016-01-01 00:10:00+00:00 0
2 5.71 2016-01-01 00:20:00+00:00 0
3 6.07 2016-01-01 00:30:00+00:00 0
4 5.75 2016-01-01 00:40:00+00:00 0

Utilizando ISIN

In [20]:
df_original[df_original.Value.isin([6])].head()
Out[20]:
Value Timestamp Quality
203 6.0 2016-01-02 09:50:00+00:00 0

Utilizando loc

In [21]:
df_original.loc[(df_original.Value > 10) & (df_original.Quality == 0)].head()
Out[21]:
Value Timestamp Quality
393 10.33 2016-01-03 17:30:00+00:00 0
394 11.18 2016-01-03 17:40:00+00:00 0
395 11.14 2016-01-03 17:50:00+00:00 0
397 10.40 2016-01-03 18:10:00+00:00 0
398 10.76 2016-01-03 18:20:00+00:00 0

Eixos

axis 0 = linhas | axis 1 = colunas

eixo 0 das linhas trabalha de cima para baixo

eixo 1 das colunas trabalha da esquerda para direita

Media das colunas numericas, ou seja, de cima para baixo

In [22]:
# 'index' é um alias para axis 0
print(df_original.mean(axis=0))
#conferir se a media estar certa
df_original.describe()
Value      6.456759
Quality    0.000000
dtype: float64
Out[22]:
Value Quality
count 432.000000 432.0
mean 6.456759 0.0
std 1.637152 0.0
min 0.870000 0.0
25% 5.687500 0.0
50% 6.490000 0.0
75% 7.270000 0.0
max 11.180000 0.0

Media das linhas, ou seja, esquerda para direita

In [23]:
# 'columns' é um alias para axis 1
df_original.mean(axis=1).head()
Out[23]:
0    3.070
1    2.875
2    2.855
3    3.035
4    2.875
dtype: float64
calculo para provar que 'df_original.mean(axis=1).head()' fez a média da linha somando as colunas numéricas
In [24]:
media_linha = (df_original['Quality'][0] + df_original['Value'][0])/2
media_linha
Out[24]:
3.069999933242798

soma das colunas

In [25]:
soma = df_original.sum(axis=1)
soma.head()
Out[25]:
0    6.14
1    5.75
2    5.71
3    6.07
4    5.75
dtype: float64

soma das linhas

In [26]:
soma = df_original.sum(axis=0)
soma.head()
Out[26]:
Value      2789.320068
Quality       0.000000
dtype: float64

TIPANDO COLUNAS

In [27]:
df_original.dtypes
Out[27]:
Value                    float32
Timestamp    datetime64[ns, UTC]
Quality                    int64
dtype: object
In [28]:
strings = []

for item in df_original.Value:
    a = str(item)
    strings.append(a)
    
df_nova_coluna = df_original
df_nova_coluna['Value_string'] = pd.Series(strings)

#Note que o DF df_original está com a coluna Value_string criada em df_nova_coluna, por isso no python é preciso copiar a lista
df_original.head()
Out[28]:
Value Timestamp Quality Value_string
0 6.14 2016-01-01 00:00:00+00:00 0 6.139999866485596
1 5.75 2016-01-01 00:10:00+00:00 0 5.75
2 5.71 2016-01-01 00:20:00+00:00 0 5.710000038146973
3 6.07 2016-01-01 00:30:00+00:00 0 6.070000171661377
4 5.75 2016-01-01 00:40:00+00:00 0 5.75

voltando df_original para o padrão

In [29]:
df_original.drop(['Value_string'], axis=1, inplace=True)
df_original.dtypes
Out[29]:
Value                    float32
Timestamp    datetime64[ns, UTC]
Quality                    int64
dtype: object

Copiando para outro DF

In [30]:
df_nova_coluna = df_original.copy()
df_nova_coluna['Value_string'] = pd.Series(strings)
df_nova_coluna.dtypes
Out[30]:
Value                       float32
Timestamp       datetime64[ns, UTC]
Quality                       int64
Value_string                 object
dtype: object

Converter Value para string mostra informações extras, como é possivel ver em Value_string o número de valores únicos(298)

In [31]:
df_nova_coluna.describe(include='all')
Out[31]:
Value Timestamp Quality Value_string
count 432.000000 432 432.0 432
unique NaN 432 NaN 298
top NaN 2016-01-01 20:40:00+00:00 NaN 6.090000152587891
freq NaN 1 NaN 5
mean 6.456759 NaN 0.0 NaN
std 1.637152 NaN 0.0 NaN
min 0.870000 NaN 0.0 NaN
25% 5.687500 NaN 0.0 NaN
50% 6.490000 NaN 0.0 NaN
75% 7.270000 NaN 0.0 NaN
max 11.180000 NaN 0.0 NaN

MÉTODOS STRINGS

Os métodos strings no Pandas são acessados via .str

fazendo um filtro por string

In [32]:
df_nova_coluna[df_nova_coluna.Value_string.str.contains('21.')].head()
Out[32]:
Value Timestamp Quality Value_string
26 5.66 2016-01-01 04:20:00+00:00 0 5.659999847412109
33 5.16 2016-01-01 05:30:00+00:00 0 5.159999847412109
48 2.21 2016-01-01 08:00:00+00:00 0 2.2100000381469727
71 5.41 2016-01-01 11:50:00+00:00 0 5.409999847412109
74 5.91 2016-01-01 12:20:00+00:00 0 5.909999847412109

Aprofundando em DataTypes

In [33]:
df_nova_coluna2 = df_nova_coluna.copy()
df_nova_coluna2.dtypes
Out[33]:
Value                       float32
Timestamp       datetime64[ns, UTC]
Quality                       int64
Value_string                 object
dtype: object

Alterando o tipo da coluna Quality para object(string)

In [34]:
df_nova_coluna2['Quality'] = df_nova_coluna2.Quality.astype(object)
df_nova_coluna2.dtypes
Out[34]:
Value                       float32
Timestamp       datetime64[ns, UTC]
Quality                      object
Value_string                 object
dtype: object

Manipulando string para pegar somente a parte inteira de Value_string

In [35]:
df_nova_coluna2['Value_int'] = df_nova_coluna2.Value_string.str.split('.').str[0]
df_nova_coluna2.head()
Out[35]:
Value Timestamp Quality Value_string Value_int
0 6.14 2016-01-01 00:00:00+00:00 0 6.139999866485596 6
1 5.75 2016-01-01 00:10:00+00:00 0 5.75 5
2 5.71 2016-01-01 00:20:00+00:00 0 5.710000038146973 5
3 6.07 2016-01-01 00:30:00+00:00 0 6.070000171661377 6
4 5.75 2016-01-01 00:40:00+00:00 0 5.75 5
In [36]:
df_nova_coluna2.describe(include='all')
Out[36]:
Value Timestamp Quality Value_string Value_int
count 432.000000 432 432.0 432 432
unique NaN 432 1.0 298 12
top NaN 2016-01-01 20:40:00+00:00 0.0 6.090000152587891 6
freq NaN 1 432.0 5 152
mean 6.456759 NaN NaN NaN NaN
std 1.637152 NaN NaN NaN NaN
min 0.870000 NaN NaN NaN NaN
25% 5.687500 NaN NaN NaN NaN
50% 6.490000 NaN NaN NaN NaN
75% 7.270000 NaN NaN NaN NaN
max 11.180000 NaN NaN NaN NaN

expand = True são retornadas duas colunas, uma com a parte interia e outra com a parte fracionária

In [37]:
df_nova_coluna2.Value_string.str.split('.', expand=True).head()
Out[37]:
0 1
0 6 139999866485596
1 5 75
2 5 710000038146973
3 6 070000171661377
4 5 75

Converter uma serie boolean em integer

In [38]:
todas_ocorrencias = df_nova_coluna2.Value_string.str.contains('21.').astype(int)
print(type(todas_ocorrencias))

#numero de ocorrencias de valores que contenham 21.
todas_ocorrencias.sum()
<class 'pandas.core.series.Series'>
Out[38]:
24

TRATANDO VALORES NULOS (NaN)

Como este dataframe não possui valores NaN, adicionei alguns quando o valor da coluna Value_int for igual a 5

In [39]:
df_nova_coluna2.loc[df_nova_coluna2.Value_int == '5', 'Value_int'] = np.nan

Saber quantidade de nulos em uma coluna

Agora a coluna Value_int possui 99 NaN, ou seja, anteriormente possuia 99 itens com valor 5

In [40]:
df_nova_coluna2.isnull().sum()
Out[40]:
Value            0
Timestamp        0
Quality          0
Value_string     0
Value_int       99
dtype: int64

Filtrar para verificar quais linhas tem nulos

In [41]:
df_nova_coluna2[df_nova_coluna2.Value_int.isnull()].tail()
Out[41]:
Value Timestamp Quality Value_string Value_int
359 5.65 2016-01-03 11:50:00+00:00 0 5.650000095367432 NaN
360 5.58 2016-01-03 12:00:00+00:00 0 5.579999923706055 NaN
361 5.87 2016-01-03 12:10:00+00:00 0 5.869999885559082 NaN
362 5.45 2016-01-03 12:20:00+00:00 0 5.449999809265137 NaN
364 5.88 2016-01-03 12:40:00+00:00 0 5.880000114440918 NaN
In [42]:
df_nova_coluna2.shape[0]
Out[42]:
432

Remover linhas que contenham valores NaN(nulo)

Com dropna inplace = True, altera o dataframe original

how = 'any' -> se existir um NaN na linha então deleta toda a linha

In [43]:
df_linhas_removidas = df_nova_coluna2.dropna(how='any')
df_linhas_removidas.shape[0]
Out[43]:
333

how = 'all' -> se todos os valores da linha for NaN então deleta a linha

não foi deletado nada porque não existe uma linha com todos valores NaN

In [44]:
A = df_nova_coluna2.dropna(how='all')
A.shape
Out[44]:
(432, 5)

Utilizando parametro subset, que recebe uma lista

não foi deletado nada porque não existe uma linha Value_int E Timestamp NaN

In [45]:
A = df_nova_coluna2.dropna(subset=['Value_int', 'Timestamp'],how='all')
A.shape
Out[45]:
(432, 5)

Foi deletado porque existe uma linha Value_int OU Timestamp NaN

In [46]:
B = df_nova_coluna2.dropna(subset=['Value_int', 'Timestamp'],how='any')
B.shape
Out[46]:
(333, 5)

value_counts SEM contar os valores NaN

In [47]:
df_nova_coluna2.Value_int.value_counts().head(10)
Out[47]:
6     152
7      83
8      28
2      15
4      13
9      13
10     11
3      10
1       5
11      2
Name: Value_int, dtype: int64

value_counts INCLUINDO os valores NaN

In [48]:
df_nova_coluna2.Value_int.value_counts(dropna=False).head(10)
Out[48]:
6      152
NaN     99
7       83
8       28
2       15
4       13
9       13
10      11
3       10
1        5
Name: Value_int, dtype: int64

Preenchendo os valores que são NaN com -1

In [49]:
C = df_nova_coluna2.fillna(value=-1)
C[C.Value_int == -1].tail()
Out[49]:
Value Timestamp Quality Value_string Value_int
359 5.65 2016-01-03 11:50:00+00:00 0 5.650000095367432 -1
360 5.58 2016-01-03 12:00:00+00:00 0 5.579999923706055 -1
361 5.87 2016-01-03 12:10:00+00:00 0 5.869999885559082 -1
362 5.45 2016-01-03 12:20:00+00:00 0 5.449999809265137 -1
364 5.88 2016-01-03 12:40:00+00:00 0 5.880000114440918 -1