data science do zero: primeiras regras com o python

442

Upload: others

Post on 11-Sep-2021

11 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Data Science do zero: Primeiras regras com o Python
Page 2: Data Science do zero: Primeiras regras com o Python

Acompradesteconteúdonãoprevêatendimentoefornecimentodesuportetécnicooperacional,instalaçãoouconfiguraçãodosistemadeleitordeebooks.Emalgunscasos,edependendodaplataforma,osuportepoderáserobtidocomofabricantedoequipamentoe/oulojadecomérciodeebooks.

Page 3: Data Science do zero: Primeiras regras com o Python
Page 4: Data Science do zero: Primeiras regras com o Python

DataSciencedoZeroCopyright©2016daStarlinAltaEditoraeConsultoriaEireli.ISBN:978-85-508-0387-6

Translated fromoriginalDataScience fromScratchbyJoelGrus.Copyright©2015byO’ReillyMedia.ISBN978-1-491-90142-7.ThistranslationispublishedandsoldbypermissionofO’ReillyMedia,Inc.,theownerofallrightstopublishandsellthesame.PORTUGUESElanguageeditionpublishedbyStarlinAltaEditoraeConsultoriaEireli,Copyright©2016byStarlinAltaEditoraeConsultoriaEireli.

TodososdireitosestãoreservadoseprotegidosporLei.Nenhumapartedestelivro,semautorizaçãopréviapor escrito da editora, poderá ser reproduzida ou transmitida. A violação dos Direitos Autorais é crimeestabelecidonaLeinº9.610/98ecompuniçãodeacordocomoartigo184doCódigoPenal.

Aeditoranãoseresponsabilizapeloconteúdodaobra,formuladaexclusivamentepelo(s)autor(es).

Marcas Registradas: Todos os termos mencionados e reconhecidos como Marca Registrada e/ouComercialsãoderesponsabilidadedeseusproprietários.Aeditorainformanãoestarassociadaanenhumprodutoe/oufornecedorapresentadonolivro.

EdiçãorevisadaconformeoAcordoOrtográficodaLínguaPortuguesade2009.

Obra disponível para venda corporativa e/ou personalizada. Para mais informações, fale [email protected]

ProduçãoEditorialEditoraAltaBooks

ProdutorEditorialClaudiaBragaThiêAlves

ProdutorEditorial(Design)AurélioCorrêa

GerênciaEditorialAndersonVieira

SupervisãodeQualidadeEditorialSergiodeSouza

AssistenteEditorialCarolinaGiannini

[email protected]

GerênciadeCaptaçãoeContrataçã[email protected]

VendasAtacadoeVarejoDanieleFonsecaVivianePaivacomercial@altabooks.com.br

Page 5: Data Science do zero: Primeiras regras com o Python

[email protected]

EquipeEditorialBiancaTeodoroChristianDannielIzabelliCarvalhoJessicaCarvalhoJulianadeOliveiraRenanCastro

TraduçãoWelingtonNascimento

CopidesqueVivianSbravatti

RevisãoGramaticalAnaPauladaFonseca

RevisãoTécnicaRonaldod’AvilaRoenickEngenheiro de Eletrônica pelo InstitutoMilitar de Engenharia(IME)

DiagramaçãoCláudioFrota

Erratas e arquivos de apoio: No site da editora relatamos, com a devida correção, qualquer erroencontradoemnossos livros,bemcomodisponibilizamosarquivosdeapoioseaplicáveisàobraemquestão.

Acesseositewww.altabooks.com.breprocurepelotítulodolivrodesejadoparateracessoàserratas,aosarquivosdeapoioe/ouaoutrosconteúdosaplicáveisàobra.

SuporteTécnico:A obra é comercializada na forma emque está, semdireito a suporte técnico ouorientaçãopessoal/exclusivaaoleitor.

DadosInternacionaisdeCatalogaçãonaPublicação(CIP)VagnerRodolfoCRB-8/9410

G885dGrus,JoelData Science doZero [ recurso eletrônico ] / JoelGrus; traduzido por

WelingtonNascimento.-RiodeJaneiro:AltaBooks,2016.336p.:il.;3,8MB.

Traduçãode:DataScienceFromScratch:FirstPrincipleswithPythonIncluiíndice.ISBN:978-85-508-0387-6(Ebook)

Page 6: Data Science do zero: Primeiras regras com o Python

1. Matemática. 2. Programação. 3. Análise de dados. I. Nascimento,Welington.II.Título.

CDD005.13CDU004.655.3

RuaViúvaCláudio,291-BairroIndustrialdoJacaréCEP:20.970-031-RiodeJaneiro(RJ)Tels.:(21)3278-8069/3278-8419www.altabooks.com.br—[email protected]/altabooks—www.instagram.com/altabooks

Page 7: Data Science do zero: Primeiras regras com o Python
Page 8: Data Science do zero: Primeiras regras com o Python

1.

2.

Sumário

Prefácio

IntroduçãoAAscensãodosDadosOQueÉDataScience?MotivaçãoHipotética:DataSciencester

EncontrandoConectores-ChaveCientistasdeDadosQueVocêTalvezConheçaSalárioseExperiênciaContasPagasTópicosdeInteresseEmDiante

CursoRelâmpagodePythonOBásico

IniciandoemPythonPythonZenFormataçãodeEspaçoemBrancoMódulosAritméticaFunçõesStrings(cadeiasdecaracteres)ExceçõesListasTuplasDicionáriosConjuntosControledeFluxo

Page 9: Data Science do zero: Primeiras regras com o Python

3.

4.

5.

VeracidadeNãoTãoBásico

OrdenaçãoCompreensõesdeListaGeradoreseIteradoresAleatoriedadeExpressõesRegularesProgramaçãoOrientadaaObjetoFerramentasFuncionaisEnumeração(enumerate)DescompactaçãodeZipeArgumentosargsekwargsBem-vindoàDataSciencester!

ParaMaisEsclarecimentos

VisualizandoDadosmatplotlibGráficosdeBarraGráficosdeLinhasGráficosdeDispersãoParaMaisEsclarecimentos

ÁlgebraLinearVetoresMatrizesParaMaisEsclarecimentos

EstatísticaDescrevendoumConjuntoÚnicodeDados

TendênciasCentraisDispersão

CorrelaçãoParadoxodeSimpsonAlgunsOutrosPontosdeAtençãosobreCorrelaçãoCorrelaçãoeCausalidadeParaMaisEsclarecimentos

Page 10: Data Science do zero: Primeiras regras com o Python

6.

7.

8.

9.

ProbabilidadeDependênciaeIndependênciaProbabilidadeCondicionalTeoremadeBayesVariáveisAleatóriasDistribuiçõesContínuasADistribuiçãoNormalOTeoremadoLimiteCentralParaMaisEsclarecimentos

HipóteseeInferênciaTesteEstatísticodeHipóteseExemplo:LançarUmaMoedap-valuesIntervalosdeConfiançaP-HackingExemplo:ExecutandoumTesteA/BInferênciaBayesianaParaMaisEsclarecimentos

GradienteDescendenteAIdeiaPorTrásdoGradienteDescendenteEstimandooGradienteUsandooGradienteEscolhendooTamanhodoPróximoPassoJuntandoTudoGradienteDescendenteEstocásticoParaMaisEsclarecimentos

ObtendoDadosstdinestdoutLendoArquivos

OBásicodeArquivosTextoArquivosdelimitados

ExtraindoDadosdaInternetHTMLeSuaSubsequentePesquisa

Page 11: Data Science do zero: Primeiras regras com o Python

10.

11.

12.

13.

Exemplo:LivrosO’ReillySobreDadosUsandoAPIs

JSON(eXML)UsandoUmaAPINãoAutenticadaEncontrandoAPIs

Exemplo:UsandoasAPIsdoTwitterObtendoCredenciais

ParaMaisEsclarecimentos

TrabalhandocomDadosExplorandoSeusDados

ExplorandoDadosUnidimensionaisDuasDimensõesMuitasDimensões

LimpandoeTransformandoManipulandoDadosRedimensionandoReduçãodaDimensionalidadeParaMaisEsclarecimentos

AprendizadodeMáquinaModelagemOQueÉAprendizadodeMáquina?SobreajusteeSub-AjustePrecisãoCompromissoentrePolarizaçãoeVariânciaRecursosExtraçãoeSeleçãodeCaracterísticaParaMaisEsclarecimentos

K–VizinhosMaisPróximosOModeloExemplo:LinguagensFavoritasAMaldiçãodaDimensionalidadeParaMaisEsclarecimentos

NaiveBayesUmFiltrodeSpamMuitoEstúpido

Page 12: Data Science do zero: Primeiras regras com o Python

14.

15.

16.

17.

UmFiltrodeSpamMaisSofisticadoImplementaçãoTestandoNossoModeloParaMaisEsclarecimentos

RegressãoLinearSimplesOModeloUsandooGradienteDescendenteEstimativaMáximadaProbabilidadeParaMaisEsclarecimentos

RegressãoMúltiplaOModeloMaisSuposiçõesdoModelodosMínimosQuadradosAjustandooModeloInterpretandooModeloOBenefíciodoAjusteDigressão:AInicializaçãoErrosPadrõesdeCoeficientesdeRegressãoRegularizaçãoParaMaisEsclarecimentos

RegressãoLogísticaOProblemaAFunçãoLogísticaAplicandooModeloOBenefíciodoAjusteMáquinadeVetordeSuporteParaMaisEsclarecimentos

ÁrvoresdeDecisãoOQueÉumaÁrvoredeDecisão?EntropiaAEntropiadeumaPartiçãoCriandoumaÁrvoredeDecisãoJuntandoTudoFlorestasAleatórias

Page 13: Data Science do zero: Primeiras regras com o Python

18.

19.

20.

21.

22.

ParaMaioresEsclarecimentos

RedesNeuraisPerceptronsRedesNeuraisFeed-ForwardBackpropagationExemplo:DerrotandoumCAPTCHAParaMaisEsclarecimentos

AgrupamentoAIdeiaOModeloExemplo:EncontrosEscolhendokExemplo:AgrupandoCoresAgrupamentoHierárquicoBottom-upParaMaisEsclarecimentos

ProcessamentodeLinguagemNaturalNuvensdePalavrasModelosn-gramasGramáticasUmAdendo:AmostragemdeGibbsModelagemdeTópicosParaMaisEsclarecimentos

AnálisedeRedeCentralidadedeIntermediaçãoCentralidadedeVetorPróprio

MultiplicaçãodeMatrizesCentralidade

GráficosDirecionadosePageRankParaMaisEsclarecimentos

SistemasRecomendadoresCuradoriaManualRecomendandoOQueéPopular

Page 14: Data Science do zero: Primeiras regras com o Python

23.

24.

25.

FiltragemColaborativaBaseadanoUsuárioFiltragemColaborativaBaseadaemItensParaMaisEsclarecimentos

BasesdeDadoseSQLCREATETABLEeINSERTUPDATEDELETESELECTGROUPBYORDERBYJOINSubconsultasÍndicesOtimizaçãodeConsultaNoSQLParaMaisEsclarecimentos

MapReduceExemplo:ContagemdePalavrasPorqueMapReduce?MapReduceMaisGeneralizadoExemplo:AnalisandoAtualizaçõesdeStatusExemplo:MultiplicaçãodeMatrizUmAdendo:CombinadoresParaMaisEsclarecimentos

VáemFrenteePratiqueDataScienceIPythonMatemáticaNãoDoZero

NumPypandasscikit-learnVisualizaçãoR

Page 15: Data Science do zero: Primeiras regras com o Python

EncontreDadosPratiqueDataScience

HackerNewsCarrosdeBombeirosCamisetasEVocê?

Page 16: Data Science do zero: Primeiras regras com o Python

•••

Prefácio

DataScienceData science tem sido chamada de “o emprego mais sexy do Século 21”(http://bit.ly/1Bqe-1WY), provavelmente por alguém que nunca tenha visitadoumquarteldocorpodebombeiros.Dequalquerforma,datascienceéumcampoem evidência e está em alta; não requer muita investigação para encontrarprognósticos de analistas de que, nos próximos dez anos, precisaremos debilhõesebilhõesdecientistasdedadosamaisdoquepossuímosatualmente.

Masoqueédatascience?Afinaldecontas,nãoconseguimosproduzircientistasdedados se não soubermosoque realmente é.De acordo comodiagramadeVenn (http://drewconway.com/zia/2013/3/26/the-data-science-venn-diagr), umtantofamosonestaárea,datascienceseencontranainterseçãode:

HabilidadesdehackerConhecimentodeestatísticaematemáticaCompetênciasignificativa

Originalmente,planejeiescreverumlivroabordandoostrês,maseurapidamentepercebi que uma abordagem completa de “competência significativa” exigiriadezenasdemilharesdepáginas.Assim,eudecidifocarnosdoisprimeiros.Meuobjetivoéajudá-loadesenvolverhabilidadesdehacker,asquaisvocêprecisaráparainiciarapráticaemdatascience.Meuoutroobjetivoéfazervocêsesentirconfortávelcommatemáticaeestatística,quesãoabasededatascience.

De alguma forma, este livro é uma grande ambição. A melhor maneira deaprender a hackear é hackeando coisas. Ao ler este livro, você terá um bomentendimentodecomoeuhackeioascoisas,quetalveznãosejaamelhorformapara você. Você entenderá quais ferramentas eu uso que talvez não sejam as

Page 17: Data Science do zero: Primeiras regras com o Python

melhores para você.Vocêverá comoeu abordoos problemas comdados, quetalveznãosejaamelhorabordagemparavocê.Aintenção(eaesperança)équemeus exemplos inspirarão você a experimentar as coisas do seu jeito. Todo ocódigo e dados deste livro estão disponíveis no GitHub(https://github.com/joelgrus/data-science-from-scratch)paraajudar.

Domesmomodo, amelhormaneira de aprendermatemática é praticando.Naverdade, este não é um livro de matemática e, na maior parte, nós não“praticaremosmatemática”.Noentanto,vocênãopodepraticardatasciencesemter algum entendimento de probabilidade, estatística e álgebra linear. Issosignifica que, quando necessário, vamos a fundo nas equações matemáticas,intuiçõesmatemáticas, axiomasmatemáticos e versões cartunescas de grandesideiasmatemáticas.Esperoquevocênãotenhamedodeirfundocomigo.

Durante todo o livro, também espero que você veja que brincar com dados édivertido, porque, bem, brincar com dados é divertido! ‘Especialmente secomparadoaalgumasalternativas,comodeclaraçãodeimpostosouexploraçãodecarvão.’

Page 18: Data Science do zero: Primeiras regras com o Python

•••

DoZeroExistemváriaseváriasbibliotecas,estruturas,módulosekitsdeferramentasdedatasciencequeimplementamdemodoeficienteosmaiscomuns(etambémosmenoscomuns)algoritmosetécnicas.Sevocêsetornarumcientistadedados,seráíntimodeNumPy,descikit-learn,depandasedediversasoutrasbibliotecas.Elas são ótimas para praticar data science e também ótimas para começar apraticarsementenderdefatooqueédatascience.

Nestelivro,abordaremosdatasciencedozero.Issosignificaqueconstruiremosferramentaseimplementaremosalgoritmosàmão,afimdeentendê-losmelhor.Eumeempenheibastanteemcriar implementaçõeseexemplosquesãoclaros,bem comentados e legíveis. Na maioria dos casos, as ferramentas queconstruiremos serão esclarecedoras,maspoucopráticas.Elas funcionarãobemem pequenos conjuntos de dados,mas fracassarão nas escalas encontradas naweb.

Nodecorrer do livro, eu indicarei bibliotecas que você talvez use para aplicartaistécnicasparaaumentarosconjuntosdedados.Porém,nãoasusaremosaqui.

Háumsólidodebatesobrequalamelhorlinguagemparaaprenderdatascience.Muitos acreditam que é a linguagem de programação estatística R. (Achamosque essas pessoas estão erradas.) Poucos sugerem Java ou Scala. Contudo,Pythonéaescolhaevidente.

Pythonpossuidiversosrecursosqueotornammaisadequadoparaoaprendizado(eprática)dedatascience:

Égratuito.Érelativamentesimplesdecodificar(e,oprincipal,deentender).Possuimuitasbibliotecasúteisrelacionadasaodatascience.

FicoreceosoaodizerquePythonéminhalinguagemdeprogramaçãofavorita.Há outras linguagens que consideromais agradáveis,mais bemprojetadas, ouapenasmaisdivertidasdetrabalhar.E,aindaassim,todavezqueeucomeçoumprojeto novo de data science, eu acabo usando Python. Toda vez que preciso

Page 19: Data Science do zero: Primeiras regras com o Python

fazerumprotótiporápidoquefuncione,euacabousandoPython.Etodavezquequero demonstrar conceitos precisos de data science, de maneira fácil deentender,acabousandoPython.Destaforma,olivrousaPython.

OobjetivodestelivronãoéensinarPython.(Apesardeserbemóbvioque,aoler este livro, você aprenderá umpouco dePython.) Irei levá-lo emum cursointensivopelocapítuloquedestacaosrecursosmaisimportantesparaosnossospropósitos,mas se você não sabe nada sobre programar em Python (ou sobreprogramaçãonogeral),talvezvocêqueiraturbinarestelivrocomalgocomoumtutorial“PythonparaIniciantes”.

Orestantedestaintroduçãoaodatascienceteráamesmaabordagem—entrandoemdetalhesquandopareceressencialouesclarecedor,outrasvezesdeixandoosdetalhesparavocêdescobrirporsisó(ouprocurarnaWikipédia).

Aolongodosanos,treineiumgrandenúmerodecientistasdedados.Apesardeque nem todos eles seguiram o caminho de se tornarem cientistas de dadosninjasrockstars,osdeixeimelhoresdoquequandoosencontrei.Vimaacreditarque qualquer pessoa que tenha alguma aptidão para a matemática e algumahabilidadeparaprogramaçãotemoqueénecessárioparapraticardatascience.Tudoo que precisa é de umamente curiosa, vontade trabalhar bastante e estelivro.Portanto,estelivro.

Page 20: Data Science do zero: Primeiras regras com o Python

ConvençõesUsadasNesteLivroAsseguintesconvençõestipográficassãousadasnestelivro:

Itálico

Indicatermosnovos,URLs,endereçosdee-mail,nomeseextensõesdearquivos.

Monoespaçada

Usadapara listagensdeprogramas, e, também,dentrodo texto se referindoaos elementosdosprogramascomovariáveisounomesde funções,bancosdedados, tiposdedados,variáveisdeambiente,declaraçõesepalavras-chave.

Monoespaçadacombold

Mostracomandosououtrotextoquedeveserliteralmentedigitadopelousuário.

Monoespaçadacomitálico

Mostra texto que deve ser substituído com valores fornecidos pelo usuário ou por valoresdeterminadospelocontexto.

Esteíconesignificaumadicaousugestão.

Esteíconesignificaumaobservaçãogeral.

Esteíconesignificaumavisoouprecaução.

Page 21: Data Science do zero: Primeiras regras com o Python

UsandoexemplosdecódigoVocê pode baixar o material complementar (exemplos de código, exercícios,etc.)nositedaEditoraAltaBooks.Procurepelo títuloouISBNdolivro.Esteconteúdo também está disponível em https://github.com/joelgrus/data-science-from-scratch.Todososoutrossitesmencionadosnestaobraestãoeminglêseaeditoranãoseresponsabilizapelamanutençãoouconteúdodesitesdeterceiros.

Este livro está aqui para ajudar a realizar o trabalho. De modo geral, se oexemplodecódigoéoferecidocomele,vocêpodeusá-loemseusprogramasedocumentações.Vocênãoprecisanoscontatarparapermissãoamenosquevocêestejareproduzindoumaporçãosignificativadocódigo.Porexemplo,escreverum programa que usa vários pedações do código deste livro não precisa depermissão.Vender ou distribuir umCD-ROM com os exemplos dos livros daAltaBooksprecisadepermissão.Responderaumaperguntacitandoeste livroou um exemplo não precisa de permissão. Incorporar uma quantidadesignificativadeexemplosdecódigodestelivronadocumentaçãodoseuprodutoprecisadepermissão.

Page 22: Data Science do zero: Primeiras regras com o Python

AgradecimentosPrimeiramente, eu gostaria de agradecer a Mike Loukides por aceitar minhaproposta para este livro (e por insistir que eu o diminuísse para um tamanhorazoável).Seriamuitomaisfácilparaeleterdito“Queméestapessoaqueviveme enviando e-mails com amostras de capítulos e como faço para ela irembora?” Fico agradecido por ele não ter feito isso. Também gostaria deagradecer aminha editora,Marie Beaugureau, porme guiar pelo processo depublicação e ter um livro em um estado muito melhor do que eu teria seestivessesozinho.

Eunãopoderiaterescritoestelivroseeununcativesseaprendidodatasciencee,provavelmente, não teria aprendido se não fosse pela influência deDaveHsu,IgorTatarinov,JohnRausereorestantedaturmaFarecast.(Hátantotempoquenemseusavaonomedatascience!)AgaleralegaldaCourseratambémmerecebastantecrédito.

Também sou muito agradecido pelos meus leitores e revisores. Jay Fundlingencontrou toneladas de erros e apontou muitas explicações que não estavamclaras,eolivroestámuitomelhor(emuitomaiscorreto)graçasaele.DebashisGhoshéumheróiporchecar todasasminhasestatísticas.AndrewMusselmansugeriu diminuir os aspectos do tipo “pessoas que preferemR ao Python sãomoralmente responsáveis” do livro, que acabei achando um ótimo conselho.Trey Causey, Ryan Matthew Balfanz, Loris Mularoni, Núria Pujol, RobJefferson,MaryPatCampbell,ZachGearyeWendyGrus tambémforneceramvaliosas opiniões. Quaisquer erros remanescentes são de minha exclusivaresponsabilidade.

DevomuitoàcomunidadedoTwitter#datascience,pormeexporatoneladasdenovos conceitos, me apresentar para muitas pessoas e parar de me sentir umfracassado, tanto que eu me superei e escrevi um livro para compensar. Umagradecimento especial para Trey Causey (novamente) por (inadvertidamente)melembrardeincluirumcapítulosobreálgebralinear,eparaScanJ.Taylorpor(inadvertidamente) apontar algumas lacunas no capítulo “Trabalhando comDados”.

Page 23: Data Science do zero: Primeiras regras com o Python

Acimade tudo, eudevoum imensoagradecimentoparaGangaeMadeline.Aúnicacoisamaisdifícildoqueescreverumlivroémorarcomalguémqueestejaescrevendoum.Eunãoteriaconseguidosemoapoiodeles.

Page 24: Data Science do zero: Primeiras regras com o Python

CAPÍTULO1

Introdução

“Dados!Dados!Dados!”elegritouimpacientemente.“Nãopossofabricartijolossembarro.”—ArthurConanDoyle

Page 25: Data Science do zero: Primeiras regras com o Python

AAscensãodosDadosVivemos em um mundo que está soterrado por dados. Os websites rastreiamtodososcliquesdetodososusuários.Seusmartphoneestáfazendoumregistroda sua localização e sua velocidade a cada segundo diariamente. Atletasavaliados usam pedômetros com esteroides que estão sempre registrando suasbatidas do coração, hábitos de movimentos, dieta e padrões do sono. Carrosinteligentes coletam hábitos de direção, casas inteligentes coletam hábitos demoradia e marqueteiros inteligentes coletam hábitos de compra. A própriainternet representa um diagrama grande de conhecimento que contém (entreoutrascoisas)umaenormeenciclopédiadereferênciascruzadas:basesdedadosespecíficos de domínio sobre filmes,música, resultados de esportes,máquinasdepinball,memesecoquetéis;emuitasestatísticasdogoverno(algumasdelassãoverdades!)sobretantosgovernosquecausariamumnónasuacabeça.

Soterrados sob esses dados estão as respostas para as inúmeras questões queninguémnuncapensouemperguntar.Nestelivro,aprenderemoscomoencontrá-las.

Page 26: Data Science do zero: Primeiras regras com o Python

OQueÉDataScience?Háumapiadaquedizqueumcientistadedadoséalguémquesabemaissobreestatística do que um cientista da computação e mais sobre ciência dacomputação do que um estatístico (eu não disse que a piada era boa). Naverdade,algunscientistasdedadossão—paratodosospropósitospráticos—estatísticos, enquanto outros são quase indistinguíveis dos engenheiros desoftware.Algunssãoexpertsemaprendizadodemáquina,enquantooutrosnãoconseguiram aprendermuita coisa sobre o assunto.Alguns sãoPhDs comumimpressionante registro de publicações, enquanto outros nunca leram umtrabalhoacadêmico(apesardeserumavergonha).Resumindo,basicamentenãoimporta como você define data science, pois você encontrará praticantes paraquemadefiniçãoestátotaleabsolutamenteerrada.

Dequalquer forma, nãopermitiremosque issonos impeçade tentar.Digamosque um cientista de dados seja alguém que extrai conhecimento de dadosdesorganizados. Omundo de hoje está cheio de pessoas tentando transformardadosemconhecimento.

Por exemplo, o site de namoro OkCupid pede que seus membros respondammilharesdeperguntasa fimdeencontrarascombinaçõesmaisadequadasparaeles.Mastambémanalisataisresultadosparadescobrirperguntasaparentementeinócuas as quais você poderia perguntar para alguém e descobrir qual apossibilidade de essa pessoa dormir com você no primeiro encontro(http://bit.ly/1EQU0hI).

O Facebook pede que você adicione sua cidade natal e sua localização atual,supostamente para facilitar que seus amigos o encontrem e se conectem comvocê.Porém,ele tambémanalisaessas localizaçõespara identificarpadrõesdemigraçãoglobal(http://on.fb.me/1EQTq3A)eondevivemosfã-clubesdostimesdefutebol(http://on.fb.me/1EQTvnO).

Como uma grande empresa, a Target rastreia suas encomendas e interações,tanto online como na loja física. Ela usa os dados em um modelo preditivo(http://nyti.ms/1EQTznL) para saber quais clientes estão grávidas a fim demelhorarsuaofertadeartigosrelacionadosabebês.

Page 27: Data Science do zero: Primeiras regras com o Python

Em 2012, a campanha do Obama empregou muitos cientistas de dados quemineraramosdadoseexperimentaramumaformadeidentificaroseleitoresqueprecisavamdeumaatençãoextra,otimizarprogramaserecursosparaacaptaçãode fundos de doadores específicos e focando esforços para votos ondeprovavelmenteelesteriamsidoúteis.Normalmente,édecomumacordopensarque esses esforços tiveram um papel importante na reeleição do presidente, oque significa que é seguro apostar que as campanhas políticas do futuro setornarão cada vez mais dependentes de dados, resultando em uma corridaarmamentistasemfimdedatascienceecoletadedados.

Agora,antesquevocêsesintamuitoexausto:algunscientistasdedadostambémusamsuashabilidadesparaobem,ocasionalmente—usarosdadosparatornaro governo mais eficiente (http://bit.ly/1EQTGiW), ajudar os desabrigados(http://bit.ly/1EQTIYl),emelhorarasaúdepública(http://bit.ly/1EQTPTv).Mas,certamente,nãoafetarásuacarreirasevocêgostadeencontraramelhormaneiradefazeropúblicoclicaremseusanúncios.

Page 28: Data Science do zero: Primeiras regras com o Python

MotivaçãoHipotética:DataSciencesterParabéns!VocêacaboudesercontratadoparaliderarosesforçosdedatasciencenaDataSciencester,aredesocialparacientistasdedados.

Apesardeserparaoscientistasdedados,aDataSciencesternuncainvestiuemconstruir sua própria atividade de data science (na verdade, aDataSciencesternunca investiu em construir seu próprio produto). Esse será seu trabalho! Nodecorrerdo livro,aprenderemossobreosconceitosdedatascienceaoresolverproblemascomosquaisvocêsedeparanotrabalho.Algumasvezes,olharemospara os dados explicitamente fornecidos pelo usuário, outras vezes olharemosparaosgeradosporsuasinteraçõescomumsitee,àsvezes,olharemosparaosdadosdosexperimentosqueprojetaremos.

E, devido à DataSciencester possuir uma forte mentalidade de “não-foi-inventado-aqui”, nós construiremos nossas próprias ferramentas do zero. Nofinal,vocêteráumsólidoentendimentodosfundamentosdedatascience.Vocêestaráprontoparaaplicar suashabilidadesemsuaempresacomumapremissamenos duvidosa, ou em qualquer outro problema que vier a despertar seuinteresse.

Bem-vindoabordoeboasorte!‘Vocêpodeusarjeansàssextaseotoaleteénofinaldocorredoràdireita.’

EncontrandoConectores-ChaveÉseuprimeirodiade trabalhonaDataSciencestereovice-presidentedeRede(networking)estácheiodeperguntassobreseususuários.Atéagora,elenãoteveninguémparaperguntar,entãoeleestámuitoempolgadoemtervocêaqui.

Particularmente,elequerquevocêidentifiquequemsãoos“conectores-chave”entreos cientistasdedados.Para isso, ele lhedáumapartede toda a rededaDataSciencester. Na vida real, você geralmente não recebe os dados de queprecisa.OCapítulo9évoltadoparaaobtençãodedados.

Com o que se parece essa parte dos dados? Ela consiste em uma lista deusuários,cadaumrepresentadoporumdictquecontémumid(umnúmero)para

Page 29: Data Science do zero: Primeiras regras com o Python

cada usuário ou usuária e um name (que por uma das grandes coincidênciascósmicasquerimacomoiddousuário):

users=[{"id":0,"name":"Hero"},{"id":1,"name":"Dunn"},{"id":2,"name":"Sue"},{"id":3,"name":"Chi"},{"id":4,"name":"Thor"},{"id":5,"name":"Clive"},{"id":6,"name":"Hicks"},{"id":7,"name":"Devin"},{"id":8,"name":"Kate"},{"id":9,"name":"Klein"}

]

Eletambémfornecedados“amigáveis”,representadosporumalistadeparesdeIDs:

friendships=[(0,1),(0,2),(1,2),(1,3),(2,3),(3,4),(4,5),(5,6),(5,7),(6,8),(7,8),(8,9)]

Porexemplo,atupla (0,1) indicaqueocientistadedadoscoma id0(Hero)eocientistadedadoscomaid1(Dunn)sãoamigos.AredeéilustradanaFigura1-1.

Figura1-1.ArededaDataSciencester

Já que representamos nossos usuários como dicts, é fácil de aumentá-los comdadosextras.

Não fiquepresoaosdetalhesdocódigoagora.NoCapítulo2, vamos levá-lo a umcursointensivodePython.Porenquanto,tentepegarosentidogeraldoqueestamos

Page 30: Data Science do zero: Primeiras regras com o Python

fazendo.

Por exemplo, talvez nós queiramos adicionar uma lista de amigos para cadausuário.Primeironósconfiguramosapropriedadefriendsdecadausuárioemumalistavazia:

foruserinusers:user["friends"]=[]

Então,nóspovoamosalistacomosdadosdefriendships:fori,jinfriendships:

#issofuncionaporqueusers[i]éousuáriocujaidéiusers[i]["friends"].append(users[j])#adicionaicomoumamigodejusers[j]["friends"].append(users[i])#adicionajcomoumamigodei

Uma vez que o dict de cada usuário contenha uma lista de amigos, podemosfacilmente perguntar sobre nosso gráfico, como “qual é o número médio deconexões?”

Primeiro,encontramosumnúmerototaldeconexões,resumindoostamanhosdetodasaslistasdefriends:

defnumber_of_friends(user):"""quantosamigosousuáriotem?"""returnlen(user["friends"])#tamanhodalistafriend_ids

total_connections=sum(number_of_friends(user)foruserinusers)#24

Então,apenasdividimospelonúmerodeusuários:from__future__importdivision#divisãointeiraestáincompletanum_users=len(users)#tamanhodalistadeusuáriosavg_connections=total_connections/num_users#2.4

Tambéméfácildeencontraraspessoasmaisconectadas—sãoasquepossuemomaiornúmerodeamigos.

Como não há muitos usuários, podemos ordená-los de “muito amigos” para“menosamigos”:

#criaumalista(user_id,number_of_friends)num_friends_by_id=[(user["id"],number_of_friends(user))

Page 31: Data Science do zero: Primeiras regras com o Python

foruserinusers]

sorted(num_friends_by_id,#éordenadokey=lambda(user_id,num_friends):num_friends,#pornum_friendsreverse=True)#domaiorparaomenor

#cadaparé(user_id,num_friends)#[(1,3),(2,3),(3,3),(5,3),(8,3),#(0,2),(4,2),(6,2),(7,2),(9,1)]

Umamaneiradepensarsobreoquenósfizemoséumamaneiradeidentificaraspessoas que são, de alguma forma, centrais para a rede. Na verdade, o queacabamosdecomputaréumaredemétricadegraudecentralidade(Figura1-2).

Figura1-2.AredeDataSciencesterordenadapelograu

Essafiguratemavantagemdeserfácildecalcular,masnemsemprelhedáosresultados que você queria ou esperaria. Por exemplo, a rede Thor daDataSciencester(id4)possuisomenteduasconexõesenquantoqueDunn(id1)possui três. Ainda olhando para a rede, parece que Thor deveria ser maiscentralizado. No Capítulo 21, investigaremos as redes com mais detalhe, everemosnoçõesdecentralidademaiscomplexasquepodemounãocorrespondermelhorànossaintuição.

CientistasdeDadosQueVocêTalvezConheçaEnquanto você está preenchendo os papéis de admissão, a vice-presidente daFraternidadechegaasuamesa.Elaquerestimularmaisconexõesentreosseusmembros, epedequevocêdesenvolva sugestõesde“CientistasdeDadosQueVocêTalvezConheça”.

Page 32: Data Science do zero: Primeiras regras com o Python

Seu primeiro instinto é sugerir um usuário que possa conhecer amigos deamigos.Sãofáceisdecomputar:paracadaamigodeumusuário,iterasobreosamigosdaquelapessoa,ecoletatodososresultados:

deffriends_of_friend_ids_bad(user):#“foaf”éabreviaçãode“friendofafriend”return[foaf["id"]

forfriendinuser["friends"]#paracadaamigodeusuárioforfoafinfriend["friends"]]#pegacada_their_friends

Quandochamamosusers[0](Hero),eleproduz:[0,2,3,0,1,3]

Isso inclui o usuário 0 (duas vezes), uma vez que Hero é, de fato, amigo deambososseusamigos.Incluiosusuários1e2,apesardeelesjáseremamigosdoHero.E incluiousuário3duasvezes, jáqueChiéalcançávelpormeiodedoisamigosdiferentes:

print[friend["id"]forfriendinusers[0]["friends"]]#[1,2]print[friend["id"]forfriendinusers[1]["friends"]]#[0,2,3]print[friend["id"]forfriendinusers[2]["friends"]]#[0,1,3]

Saber que as pessoas são amigas-de-amigas de diversasmaneiras parece umainformaçãointeressante,entãotalveznósdevêssemosproduzirumacontagemdeamigos em comum.Definitivamente, devemos usar uma função de ajuda paraexcluiraspessoasquejásãoconhecidasdousuário:

fromcollectionsimportCounter#nãocarregadoporpadrão

defnot_the_same(user,other_user):"""doisusuáriosnãosãoosmesmossepossuemidsdiferentes"""returnuser["id"]!=other_user["id"]

defnot_friends(user,other_user):"""other_usernãoéumamigosenãoestáemuser[“friends”];issoé,seénot_the_samecomtodasaspessoasemuser[“friends”]"""returnall(not_the_same(friend,other_user)

forfriendinuser["friends"])

deffriends_of_friend_ids(user):returnCounter(foaf["id"]

forfriendinuser["friends"]#paracadaumdosmeusamigosforfoafinfriend["friends"]#quecontam*their*amigosifnot_the_same(user,foaf)#quenãosejameuandnot_friends(user,foaf))#equenãosãomeusamigos

printfriends_of_friend_ids(users[3])#Counter({0:2,5:1})

Page 33: Data Science do zero: Primeiras regras com o Python

IssodizsobreChi(id3)queelapossuidoisamigosemcomumcomHero(id0)massomenteumamigoemcomumcomClive(id5).

Como um cientista de dados, você sabe que você pode gostar de encontrarusuários com interesses similares (esse é um bom exemplo do aspecto“competência significativa”dedata science).Depoisdeperguntarpor aí, vocêconseguepôrasmãosnessedado,comoumalistadepares(user_id,interest):

interests=[(0,"Hadoop"),(0,"BigData"),(0,"HBase"),(0,"Java"),(0,"Spark"),(0,"Storm"),(0,"Cassandra"),(1,"NoSQL"),(1,"MongoDB"),(1,"Cassandra"),(1,"HBase"),(1,"Postgres"),(2,"Python"),(2,"scikit-learn"),(2,"scipy"),(2,"numpy"),(2,"statsmodels"),(2,"pandas"),(3,"R"),(3,"Python"),(3,"statistics"),(3,"regression"),(3,"probability"),(4,"machinelearning"),(4,"regression"),(4,"decisiontrees"),(4,"libsvm"),(5,"Python"),(5,"R"),(5,"Java"),(5,"C++"),(5,"Haskell"),(5,"programminglanguages"),(6,"statistics"),(6,"probability"),(6,"mathematics"),(6,"theory"),(7,"machinelearning"),(7,"scikit-learn"),(7,"Mahout"),(7,"neuralnetworks"),(8,"neuralnetworks"),(8,"deeplearning"),(8,"BigData"),(8,"artificialintelligence"),(9,"Hadoop"),(9,"Java"),(9,"MapReduce"),(9,"BigData")

]

Porexemplo,Thor(id4)nãopossuiamigosemcomumcomDevin(id7),mascompartilhamdointeresseemaprendizadodemáquina.

Éfácilconstruirumafunçãoqueencontreusuárioscomomesmointeresse:defdata_scientists_who_like(target_interest):

return[user_idforuser_id,user_interestininterestsifuser_interest==target_interest]

Funciona,masalistainteiradeinteressesdeveserexaminadaparacadabusca.Setivermosmuitosusuárioseinteresses(ousequisermosfazermuitasbuscas),seriamelhorconstruirumíndicedeinteressesparausuários:

fromcollectionsimportdefaultdict

#aschavessãointeresses,osvaloressãolistasdeuser_idscominterestsuser_ids_by_interest=defaultdict(list)

foruser_id,interestininterests:user_ids_by_interest[interest].append(user_id)

Page 34: Data Science do zero: Primeiras regras com o Python

•••

Eoutrodeusuáriosparainteresses:#aschavessãouser_ids,osvaloressãoaslistasdeinterestsparaaqueleuser_idinterests_by_user_id=defaultdict(list)

foruser_id,interestininterests:interests_by_user_id[user_id].append(interest)

Agoraficafácildescobrirquempossuiosmaioresinteressesemcomumcomumcertousuário:

Iterasobreosinteressesdousuário.Paracadainteresse,iterasobreosoutrosusuárioscomaqueleinteresse.Mantémacontagemdequantasvezesvemoscadaoutrousuário.

defmost_common_interests_with(user):returnCounter(interested_user_id

forinterestininterests_by_user_id[user["id"]]forinterested_user_idinuser_ids_by_interest[interest]ifinterested_user_id!=user["id"])

Poderíamos usar esse exemplo para construir um recurso mais rico de“CientistasdeDadosQueVocêDeveriaConhecer”baseadoemumacombinaçãode amigos e interesses emcomum.Exploraremos esses tipos de aplicações noCapítulo22.

SalárioseExperiênciaNahoraemquevocêestásaindoparaoalmoço,ovice-presidentedeRelaçõesPúblicaspergunta sevocêpode forneceralguns fatoscuriosossobrequantooscientistasdedadosrecebem.Dadosdesalárioé,defato,umtópicosensível,maseleconseguefornecerumconjuntodedadosanônimoscontendoosalary(salário)decadausuário (emdólares)e tenure (experiência)comoumcientistadedados(emanos):

salaries_and_tenures=[(83000,8.7),(88000,8.1),(48000,0.7),(76000,6),(69000,6.5),(76000,7.5),(60000,2.5),(83000,10),(48000,1.9),(63000,4.2)]

Naturalmente, o primeiro passo é traçar os dados (veremos como fazê-lo noCapítulo3).OsresultadosseencontramnaFigura1-3.

Page 35: Data Science do zero: Primeiras regras com o Python

Figura1-3.Salárioporanosdeexperiência

Fica bem claro que os que possuemmais experiência tendem a recebermais.Como você pode transformar isso em um fato curioso? A primeira ideia éanalisaramédiasalarialparacadaano:

#aschavessãoosanos,osvaloressãoaslistasdossaláriosparacadaanosalary_by_tenure=defaultdict(list)

forsalary,tenureinsalaries_and_tenures:salary_by_tenure[tenure].append(salary)

#aschavessãoosanos,cadavaloréamédiasalarialparaaqueleanoaverage_salary_by_tenure={

tenure:sum(salaries)/len(salaries)fortenure,salariesinsalary_by_tenure.items()

}

Não é muito útil, já que nenhum dos usuários possui o mesmo caso, o quesignificaqueestamosreportandoapenasossaláriosindividuaisdosusuários:

Page 36: Data Science do zero: Primeiras regras com o Python

{0.7:48000.0,1.9:48000.0,2.5:60000.0,4.2:63000.0,6:76000.0,6.5:69000.0,7.5:76000.0,8.1:88000.0,8.7:83000.0,10:83000.0}

Talvezfossemaisproveitosoagruparoscasos:deftenure_bucket(tenure):

iftenure<2:return"lessthantwo"

eliftenure<5:return"betweentwoandfive"

else:return"morethanfive"

Então,ogrupojuntaossalárioscorrespondentesparacadaagrupamento:#aschavessãoagrupamentosdoscasos,osvaloressãoaslistas#dossaláriosparaaqueleagrupamentosalary_by_tenure_bucket=defaultdict(list)

forsalary,tenureinsalaries_and_tenures:bucket=tenure_bucket(tenure)salary_by_tenure_bucket[bucket].append(salary)

E,finalmente,computaramédiasalarialparacadagrupo:#aschavessãoagrupamentosdoscasos,osvaloressão#amédiasalarialparaaqueleagrupamentoaverage_salary_by_bucket={

tenure_bucket:sum(salaries)/len(salaries)fortenure_bucket,salariesinsalary_by_tenure_bucket.iteritems()

}

queémaisinteressante:{'betweentwoandfive':61500.0,'lessthantwo':48000.0,'morethanfive':79166.66666666667}

E você tem um clichê: “os cientistas de dados com mais de cinco anos deexperiência recebem 65% amais do que os que possuem pouca ou nenhumaexperiência!”

Page 37: Data Science do zero: Primeiras regras com o Python

No entanto, nós escolhemos os casos de forma aleatória. O que realmentequeríamosfazereraorganizarumtipodeafirmaçãosobreoefeitodosalário—emmédia—deterumanoadicionaldeexperiência.Alémdetornarofatomaisintrigante, ainda permite que façamos previsões sobre salários que nãoconhecemos.ExploraremosmaisessaideianoCapítulo14.

ContasPagasAovoltarparaasuamesa,avice-presidentedaReceitaestáesperandoporvocê.Ela quer entendermelhor quais sãoos usuários quepagampor contas e quaisquenãopagam(elasabeseusnomes,masessainformaçãonãoéessencial).

Você percebe que parece haver uma correspondência entre os anos deexperiênciaeascontaspagas:

0.7paid1.9unpaid2.5paid4.2unpaid6unpaid6.5unpaid7.5unpaid8.1unpaid8.7paid10paid

Os usuários com poucos e muitos anos de experiência tendem a pagar; osusuárioscomumaquantidademedianadeexperiêncianão.

Logo, se você quisesse criar um modelo — apesar de não haver dados osuficienteparaservirdebaseparaum—vocêtalveztentasseprever“paid”paraos usuários com poucos e muitos anos de experiência, e “unpaid” para osusuárioscomquantidademedianadeexperiência:

defpredict_paid_or_unpaid(years_experience):ifyears_experience<3.0:return"paid"elifyears_experience<8.5:return"unpaid"else:return"paid"

Certamente,nósdefinimosvisualmenteoscortes.

Page 38: Data Science do zero: Primeiras regras com o Python

1.

2.3.

Com mais dados (e mais matemática), nós poderíamos construir um modeloprevendoaprobabilidadedequeumusuáriopagaria,baseadoemseusanosdeexperiência.InvestigaremosessetipodeproblemanoCapítulo16.

TópicosdeInteresseQuando seu dia está terminando, a vice-presidente da Estratégia de Conteúdopededadossobreemquaistópicososusuáriosestãomaisinteressados,paraqueelapossaplanejarocalendáriodoseublogdeacordo.Vocêjápossuiosdadosbrutosparaoprojetosugerido:

interests=[(0,"Hadoop"),(0,"BigData"),(0,"HBase"),(0,"Java"),(0,"Spark"),(0,"Storm"),(0,"Cassandra"),(1,“NoSQL”),(1,“MongoDB”),(1,“Cassandra”),(1,“HBase”),(1,“Postgres”),(2,“Python”),(2,“scikit-learn”),(2,“scipy”),(2,“numpy”),(2,“statsmodels”),(2,“pandas”),(3,“R”),(3,“Python”),(3,“statistics”),(3,“regression”),(3,“probability”),(4,“machinelearning”),(4,“regression”),(4,“decisiontrees”),(4,“libsvm”),(5,“Python”),(5,“R”),(5,“Java”),(5,“C++”),(5,“Haskell”),(5,“programminglanguages”),(6,“statistics”),(6,“probability”),(6,“mathematics”),(6,“theory”),(7,“machinelearning”),(7,“scikit-learn”),(7,“Mahout”),(7,“neuralnetworks”),(8,“neuralnetworks”),(8,“deeplearning”),(8,“BigData”),(8,“artificialintelligence”),(9,“Hadoop”),(9,“Java”),(9,“MapReduce”),(9,“BigData”)

]

Uma simples forma (e também fascinante) de encontrar os interesses maispopulareséfazerumasimplescontagemdepalavras:

Coloquecadaumemletrasminúsculas(jáqueusuáriosdiferentespodemounãoescreverseusinteressesemletrasmaiúsculas).Dividaempalavras.Conteosresultados.

Nocódigo:words_and_counts=Counter(word

foruser,interestininterestsforwordininterest.lower().split())

Issofacilitalistaraspalavrasqueocorremmaisdeumavez:forword,countinwords_and_counts.most_common():

Page 39: Data Science do zero: Primeiras regras com o Python

ifcount>1:printword,count

oqueforneceoresultadoesperado(amenosquevocêespereque“scikit-learn”possaserdivididoemduaspalavras,oquenãoforneceráoresultadoesperado):

learning3java3python3big3data3hbase2regression2cassandra2statistics2probability2hadoop2networks2machine2neural2scikit-learn2r2

VeremosformasmaisaprimoradasdeextrairtópicosdosdadosnoCapítulo20.

EmDianteFoi um primeiro dia bem proveitoso! Exausto, você sai do prédio antes quealguémpeçaalgomais.Tenhaumaboanoitedesono,porqueamanhãserádiadetreinamentoparanovos funcionários.Sim,você trabalhouumdia inteiroantesdotreinamento.CulpadoRH.

Page 40: Data Science do zero: Primeiras regras com o Python

CAPÍTULO2

CursoRelâmpagodePython

AspessoasaindasãoloucaspeloPythonmesmodepoisdevinteecincoanos,oqueédifícildeacreditar.

—MichaelPalin

Todos os funcionários novos na DataSciencester são obrigados a passar pelotreinamentodosfuncionáriosnovos,apartemaisinteressanteéquecontémumcursointensivodePython.

MasnãoéumtutorialcompreensívelsobrePython,édirecionadoadestacaraspartes da linguagem que serãomais importantes para nós (algumas delas, emgeral,nãosãoofocodostutoriaisPython).

Page 41: Data Science do zero: Primeiras regras com o Python

OBásicoIniciandoemPythonVocê pode baixar o Python em python.org (https://www.python.org/). Mas sevocê ainda não o tiver, recomendo a instalação da distribuição Anaconda(https://store.continuum.io/cshop/anaconda/), que já contém a maioria dasbibliotecasquevocêprecisaparapraticardatascience.

Nomomento da escrita deste livro, a versãomais recente do Python é a 3.4.Porém,naDataSciencester,usamosoantigoeconfiávelPython2.7.OPython3não é compatível com a versão anterior do Python 2 e muitas bibliotecasimportantes somente funcionam bem com 2.7. A comunidade do data scienceaindaestápresaao2.7,oquesignificaquenósestaremostambém.Certifique-sedeteressaversão.

SevocênãoconseguirAnaconda, instalepip(https://pypi.python.org/pypi/pip),queéumgerenciadordepacotePythonquepermitequevocêfacilmenteinstalepacotes de terceiros (precisaremos de alguns). Também vale a pena obterIPython (http://ipython.org/), o qual é um shell Python muito melhor de setrabalhar.

SevocêinstalouAnaconda,jádevevircompipeIPython.

Apenasexecute:pipinstallipython

e então procure na internet por soluções para quaisquer mensagens de errosenigmáticasquehouver.

PythonZenPython possui uma descrição Zen de seus princípios de design(http://legacy.python.org/dev/peps/pep-0020/), que você encontrar dentro doprópriointerpretadorPythonaodigitarimportthis.

Omaisdiscutidodelesé:

Deveriahaverum—depreferênciaapenasum—modoóbviodefazê-

Page 42: Data Science do zero: Primeiras regras com o Python

lo.

O código escrito de acordo com esse modo “óbvio”(que talvez não seja tãoóbvioparaumnovato) frequentementeédescritocomo“Pythonic”.Apesardeeste não ser um livro sobre Python, de vez em quando contrastaremosmodosPythonic e não-Pythonic de realizar os mesmos processos, e favoreceremossoluçõesPythonicparaosnossosproblemas.

FormataçãodeEspaçoemBrancoMuitas linguagens usam chaves para delimitar blocos de código. Python usaindentação:

foriin[1,2,3,4,5]:printi#primeiralinhaparaobloco“fori”forjin[1,2,3,4,5]:

printj#primeiralinhaparaobloco“forj”printi+j#últimalinhaparaobloco“forj”

printi#últimalinhaparaobloco“fori”print"donelooping"

IssofazcomqueocódigoPythonsejabemlegível,mastambémsignificaquevocêtemquesermuitocuidadosocomasuaformatação.Oespaçoembrancoéignorado dentro dos parênteses e colchetes, o que poder ser muito útil emcomputaçõesintermináveis:

long_winded_computation=(1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20)

eparafacilitaraleitura:list_of_lists=[[1,2,3],[4,5,6],[7,8,9]]

easier_to_read_list_of_lists=[[1,2,3],[4,5,6],[7,8,9]]

Você também pode usar uma barra invertida para indicar que uma declaraçãocontinuanapróximalinha,apesarderaramentefazermosisso:

two_plus_three=2+\3

Uma consequência da formatação do espaço embranco é que pode ser difícilcopiarecolarocódigonoPythonshell.Porexemplo,sevocêtentassecolarestecódigo:

Page 43: Data Science do zero: Primeiras regras com o Python

foriin[1,2,3,4,5]:

#notealinhaembrancoprinti

naPythonshellcomum,vocêteria:IndentationError:expectedanindentedblock

porqueointerpretadorpensaquealinhaembrancodeterminaofinaldoblocodoloopfor.

IPython tem a função mágica %paste, que copia corretamente o que quer queestejanaáreadetransferência,espaçoembrancoetudoomais.ApenasissojáéumaboarazãoparausarIPython.

MódulosAlguns recursos de Python não são carregados por padrão. Isto inclui tantorecursoscomoparteda linguagemassimcomorecursosde terceiros,quevocêbaixaporcontaprópria.Parausaressesrecursos,vocêprecisaráimport(importar)osmódulosqueoscontêm.

Umaabordagemésimplesmenteimportaroprópriomódulo:importremy_regex=re.compile("[0-9]+",re.I)

Aqui, re é o módulo que contém as funções e constantes para trabalhar comexpressões regulares.Após esse tipo de import, você somente pode acessar taisfunçõesusandooprefixore…

Sevocêjátiverumrediferentenoseucódigovocêpoderiausarumalias:importreasregexmy_regex=regex.compile("[0-9]+",regex.I)

Vocêtalvezqueirafazer issoseoseumódulo temumnomecomplicadoousevocê vai digitar bastante. Por exemplo, ao visualizar dados commatplotlib,umaconvençãopadrãoé:

importmatplotlib.pyplotasplt

Sevocêprecisardealgunsvaloresespecíficosdeummódulo,podeimportá-losexplicitamenteeusá-lossemqualificação:

Page 44: Data Science do zero: Primeiras regras com o Python

fromcollectionsimportdefaultdict,Counterlookup=defaultdict(int)my_counter=Counter()

Sevocêfosseumapessoamá,vocêpoderiaimportaroconteúdointeirodeummódulo dentro do seu conjunto de nomes, o que talvez pudesse sobrescrevervariáveisquevocêjátinhadefinido:

match=10fromreimport*#ihnão,retemumafunçãoquecombinaçãoprintmatch#"<functionre.match>"

Noentanto,jáquevocênãoéumapessoamá,vocênuncafaráisso.

AritméticaPython2.7usaadivisãode inteirosporpadrão,portanto5/2é iguala2.Quasesempreissonãoéoquequeremos,entãosemprecomeçaremosnossosarquivoscom:

from__future__importdivision

depoisdisso,5/2éiguala2.5.Todoexemplodecódigonestelivrousaessenovoestilodedivisão.Nagrandemaioriadoscasosemqueprecisaremosdedivisãodeinteiros,poderemosobtê-lacomumabarradupla5//2.

FunçõesUmafunçãoéumaregraparapegarzeroemaisentradase retornarumasaídacorrespondente.EmPython,definimosasfunçõesusandodef:

defdouble(x):"""aquiéondevocêcolocaumdocstring(cadeiadecaracteresdedocumentação)opcionalqueexplicaoqueafunçãofaz.porexemplo,estafunçãomultiplicasuaentradapor2"""returnx*2

AsfunçõesdePythonsãodeprimeiraclasse,quesignificaquepodemosatribuí-lasavariáveisepassá-lasparaasfunçõescomoquaisqueroutrosargumentos:

defapply_to_one(f):"""chamaafunçãofcom1comoseuargumento"""returnf(1)

my_double=double#refere-seàfunçãodefinidaanteriormentex=apply_to_one(my_double)#éiguala2

Page 45: Data Science do zero: Primeiras regras com o Python

Tambéméfácilcriarpequenasfunçõesanônimas,oulambdas:y=apply_to_one(lambdax:x+4)#éiguala5

Vocêpodeatribuir lambdasavariáveis, apesardequemaioriadaspessoas lhedirãoparausardef:

another_double=lambdax:2*x#nãofaçaissodefanother_double(x):return2*x#façaisso

Os parâmetros de função também podem receber argumentos padrões, que sóprecisamserespecificadosquandovocêquiserumvaloralémdopadrão:

defmy_print(message="mydefaultmessage"):printmessage

my_print("hello")#exibe'hello'my_print()#exibe'mydefaultmessage'

Àsvezeséútilespecificarargumentospelonome:defsubtract(a=0,b=0):

returna-b

subtract(10,5)#retornaSsubtract(0,5)#retorna-Ssubtract(b=5)#mesmoqueoanterior

Criaremosmuitas,muitasfunções.

Strings(cadeiasdecaracteres)Asstringspodemserdelimitadasporaspassimplesouduplas(maselasdevemcombinar):

single_quoted_string='datascience'double_quoted_string="datascience"

OPythonusaabarrainvertidaparacodificarcaracteresespeciais.Porexemplo:tab_string="\t"#representaocaracteretablen(tab_string)#é1

Sevocêquiserbarrasinvertidascomobarrasinvertidas(quevocêvênosnomesdosdiretóriosouexpressõesregularesnoWindows),vocêpodecriarumastringbrutausandor"":

not_tab_string=r"\t"#representaoscaracteres'\'e't'len(not_tab_string)#é2

Page 46: Data Science do zero: Primeiras regras com o Python

Vocêpodecriarstringsmúltiplasusandoaspastriplasouduplas:multi_line_string="""estaéaprimeiralinha.eestaéasegundaeestaéaterceira"""

ExceçõesQuandoalgodáerrado,oPythonexibeumaexceção.Senãoformanipulada,oprogramatravará.Vocêpodemanipulá-lasusandotryeexcept:

try:print0/0

exceptZeroDivisionError:print"cannotdividebyzero"

Apesar de serem consideradas ruins em muitas linguagens, as exceções sãousadaslivrementenoPythonparadarumalimpezanocódigoe,ocasionalmente,faremosomesmo.

ListasProvavelmente,aestruturadedadosmaisbásicaemPythonéa list.Umalistaéapenasumacoleçãoordenada. (Éparecida como arraydasoutras linguagens,mascomalgumasfuncionalidadesamais.)

integer_list=[1,2,3]heterogeneous_list=["string",0.1,True]list_of_lists=[integer_list,heterogeneous_list,[]]

list_length=len(integer_list)#éiguala3list_sum=sum(integer_list)#éiguala6

Vocêpodeterouconfiguraroelementon-ésimodeumalistacomcolchetes:x=range(10)#éalista[0,1,...,9]zero=x[0]#éiguala0,aslistassãoindexadasapartirde0one=x[1]#éiguala1nine=x[-1]#éiguala9,'Pythonic'paraoúltimoelementoeight=x[-2]#éiguala8,'Pythonic'paraoanterioraoúltimoelementox[0]=-1#agoraxé[-1,1,2,3,...,9]

Vocêtambémpodeusaroscolchetespararepartiraslistas:first_three=x[:3]#[-1,1,2]three_to_end=x[3:];#[3,4,...,9]one_to_four=x[1:5]#[1,2,3,4]

Page 47: Data Science do zero: Primeiras regras com o Python

last_three=x[-3:]#[7,8,9]without_first_and_last=x[1:-1];#[1,2,...,8]copy_of_x=x[:]#[-1,1,2,...,9]

OPythonpossuiooperadorinparaverificaraassociaçãoàlista:1in[1,2,3]#Verdadeiro0in[1,2,3]#Falso

Essaverificaçãoenvolveexaminaroselementosdeumalistaumdecadavez,oquesignificaquevocêprovavelmentenãodeveriausá-laamenosquevocêsaibaquesualistaépequena(ouamenosquevocênãoseimporteemquantotempoaverificaçãodurará).

Éfácilconcatenaraslistasjuntas:x=[1,2,3]x.extend([4,5,6])#xagoraé[1,2,3,4,5,6]

Sevocênãoquisermodificarxvocêpodeusarumaadiçãonalista:x=[1,2,3]y=x+[4,5,6]#yé[1,2,3,4,5,6];xnãomudou

Commaisfrequência,anexaremosumitemdecadaveznaslistas:x=[1,2,3]x.append(0)#xagoraé[1,2,3,0]y=x[-1]#éiguala0z=len(x)#éiguala4

Àsvezeséconvenientedesfazer as listas se você sabequantos elementos elaspossuem:

x,y=[1,2]#agoraxé1,yé2

apesar de que você receberá umValueError se não tiver osmesmos números deelementosdosdoislados.

Écomumusarumsublinhadoparaumvalorquevocêdescartará:_,y=[1,2]#agoray==2,nãosepreocupoucomoprimeiroelemento

TuplasSão as primas imutáveis das listas.Quase tudoquevocêpode fazer comumalista, que não envolva modificá-la, é possível ser feito em uma tupla. Vocêespecificaumatuplaaousarparênteses(ounada)emvezdecolchetes:

Page 48: Data Science do zero: Primeiras regras com o Python

my_list=[1,2]my_tuple=(1,2)other_tuple=3,4my_list[1]=3#my_listagoraé[1,3]

try:my_tuple[1]=3

exceptTypeError:print"cannotmodifyatuple"

As tuplas são uma maneira eficaz de retornar múltiplos valores a partir dasfunções:

defsum_and_product(x,y):return(x+y),(x*y)

sp=sum_and_product(2,3)#éigual(5,6)s,p=sum_and_product(5,10)#sé15,pé50

Astuplas(elistas)tambémpodemserusadasparaatribuiçõesmúltiplas:x,y=1,2#agoraxé1,yé2x,y=y,x#modoPythonicdetrocarasvariáveis;agoraxé2,yé1

DicionáriosOutraestruturafundamentaléumdicionário,queassociavalorescomchavesepermite que você recupere o valor correspondente de uma dada chaverapidamente:

empty_dict={}#Pythonicempty_dict2=dict();#menosPythonicgrades={"Joel":80,"Tim":95}#dicionárioliteral

Vocêpodeprocurarovalorparaumachaveusandocolchetes:joels_grade=grades["Joel"]#éiguala80

Mas você receberá um keyError se perguntar por uma chave que não esteja nodicionário:

try:kates_grade=grades["Kate"]

exceptKeyError:print"nogradeforKate!"

Vocêpodeverificaraexistênciadeumachaveusandoin:joel_has_grade="Joel"ingrades#Verdadeirokate_has_grade="Kate"ingrades#Falso

Page 49: Data Science do zero: Primeiras regras com o Python

Osdicionáriospossuemométodo get que retornaumvalorpadrão (emvezdelevantar uma exceção) quandovocê procura por uma chave que não esteja nodicionário:

joels_grade=grades.get("Joel",0)#éiguala80kates_grade=grades.get("Kate",0)#éiguala0no_ones_grade=grades.get("NoOne")#padrãoparapadrãoéNone

Vocêatribuiparesdevalores-chaveusandoosmesmoscolchetes:grades["Tim"]=99#substituiovalorantigogrades["Kate"]=100#adicionaumaterceiraentradanum_students=len(grades)#éiguala3

Frequentementeusaremosdicionárioscomoumasimplesmaneiraderepresentardadosestruturados:

tweet={"user":"joelgrus","text":"DataScienceisAwesome","retweet_count":100,"hashtags":["#data","#science","#datascience","#awesome","#yolo"]

}

Alémdeprocurarporchavesespecíficas,podemosolharparatodaselas:tweet_keys=tweet.keys()#listadechavestweet_values=tweet.values()#listadevalores-chavetweet_items=tweet.items()#listade(chave,valor)tuplas

"user"intweet_keys#Verdadeiro,masusalistin,maislento"user"intweet#maisPythonic,usadictin,maisrápido"joelgrus"intweet_values#Verdadeiro

Aschavesdosdicionáriosdevemserimutáveis;particularmente,vocênãopodeusar lists como chaves. Se você precisar de uma chavemultipart, você deveriausarumatupleoudescobrirumaformadetransformarumachaveemumastring.

defaultdict

Imagine que você esteja tentando contar as palavras em um documento. Ummétodoclaroécriarumdicionárionoqualaschavessãopalavraseosvaloressão contagens. Conforme você vai verificando cada palavra, você podeincrementar sua contagem se ela já estiver no dicionário e adicioná-la nodicionáriosenãoestiver:

word_counts={}

Page 50: Data Science do zero: Primeiras regras com o Python

forwordindocument:ifwordinword_counts:

word_counts[word]+=1else:

word_counts[word]=1

Você também poderia usar o método “perdão é melhor do que permissão” eapenasmanipularaexceçãoapartirdatentativadeprocurarpelachaveperdida:

word_counts={}forwordindocument:

try:word_counts[word]+=1

exceptKeyError:word_counts[word]=1

Umaterceiraabordageméusarget,quesecomportamuitobemcomaschavesperdidas:

word_counts={}forwordindocument:

previous_count=word_counts.get(word,0)word_counts[word]=previous_count+1

Tudo isso é levemente complicado, por isso o defaultdict é útil. Um defaultdict écomoumdicionário comum, exceto que, quandovocê tenta procurar por umachave que ele não possui, ele primeiro adiciona um valor para ela usando afunçãodeargumentozeroquevocêforneceuaocriá-lo.Parausardefaultdicts,vocêtemqueimportá-losdascollections:

fromcollectionsimportdefaultdict

word_counts=defaultdict(int)#int()produz0forwordindocument:

word_counts[word]+=1

Eles tambémpodem ser úteis com list ou dict ou atémesmocom suasprópriasfunções:

dd_list=defaultdict(list)#list()produzumalistavaziadd_list[2].append(1)#agoradd_listcontém{2:[1]}

dd_dict=defaultdict(dict)#dict()produzumdictvaziodd_dict["Joel"]["City"]="Seattle"#{"Joel":{"City":Seattle"}}

dd_pair=defaultdict(lambda:[0,0])dd_pair[2][1]=1#agoradd_paircontêm{2:[0,1]}

Issoseráútilquandovocêusardicionáriospara“coletar”resultadosporalguma

Page 51: Data Science do zero: Primeiras regras com o Python

chaveenãoquiserverificartodavezparaverseelaaindaexiste.

Contador

UmCounter (contador) transforma uma sequência de valores em algo parecidocomoobjetodefaultdict(int)mapeandoaschavesparaascontagens.Primeiramente,ousaremosparacriarhistogramas:

fromcollectionsimportCounterc=Counter([0,1,2,0])#cé(basicamente){0:2,1:1,2:1}

Issonosmostraumaformasimplesderesolvernossoproblemadeword_counts:word_counts=Counter(document)

UmainstânciaCounterpossuiummétodomost_commonqueéfrequentementeútil:#imprimeasdezpalavasmaiscomunsesuascontasforword,countinword_counts.most_common(10):

printword,count

ConjuntosOutra estrutura de dados é o set (conjunto), que representa uma coleção deelementosdistintos:

s=set()s.add(1)#sagoraé{1}s.add(2)#sagoraé{1,2}s.add(2)#saindaé{1,2}x=len(s)#éiguala2y=2ins#éigualaTruez=3ins#éigualaFalse

Usaremos os conjuntos por duas razões principais.A primeira é que in é umaoperaçãomuitorápidaemconjuntos.Se tivermosumagrandecoleçãode itensquequeiramosusarparaumtestedesociedade,umconjuntoémaisadequadodoqueumalista:

stopwords_list=["a","an","at"]+hundreds_of_other_words+["yet","you"]

"zip"instopwords_list#Falso,mastemqueverificartodososelementos

stopwords_set=set(stopwords_list)"zip"instopwords_set#muitorápidoparaverificar

Asegundarazãoéencontrarositensdistintosemumacoleção:

Page 52: Data Science do zero: Primeiras regras com o Python

item_list=[1,2,3,1,2,3]num_items=len(item_list)#6item_set=set(item_list)#{1,2,3}num_distinct_items=len(item_set)#3distinct_item_list=list(item_set)#[1,2,3]

Usaremossetscommenosfrequênciadoquedictselists.

ControledeFluxoComonamaioriadaslinguagensdeprogramação,vocêpodedesempenharumaaçãocondicionalmenteusandoif:

if1>2:message="ifonly1weregreaterthantwo..."

elif1>3:message="elifstandsfor'elseif'"

else:message="whenallelsefailsuseelse(ifyouwantto)"

Você também pode escrever um ternário if-then-else em uma linha, o quefaremosocasionalmente:

parity="even"ifx%2==0else"odd"

Pythonpossuiumloopwhile:x=0whilex<10:

printx,"islessthan10"x+=1

emborausaremosmaisforein:forxinrange(10):

printx,"islessthan10"

Sevocêprecisardeumalógicamaiscomplexa,podeusarcontinueebreak:forxinrange(10):

ifx==3:continue#vaiparaapróximaiteraçãoimediatamente

ifx==5:break#saidoloopcompletamente

printx

Essasaídaserá0,1,2e4.

Page 53: Data Science do zero: Primeiras regras com o Python

VeracidadeOsBooleanos em Python funcionam como namaioria das linguagens, excetoqueelessãoiniciadosporletrasmaiúsculas:

one_is_less_than_two=1<2#éigualaTruetrue_equals_false=True==False#éigualaFalse

PythonusaovalorNonepara indicarumvalornão-existente.Éparecidocomonulldasoutraslinguagens:

x=Noneprintx==None#imprimeTruemasnãoéPythonicprintxisNone#imprimeTrueeéPythonic

O Python permite que você use qualquer valor que espera por umBooleano.Todososexemplosaseguirsão“Falsos”:

False

None

[](umalistvazia)

{}(umdictvazio)

""

set()

0

0.0

Quase todo o restante pode ser tratado como True. Isso permite que você usedeclaraçõesifparatestarlistas,stringsoudicionáriosvazioseassimpordiante.Às vezes isso causa alguns pequenos bugs se você estiver esperando por estecomportamento:

s=some_function_that_returns_a_string()ifs:

first_char=s[0]else:

first_char=""

Umaformamaissimplesdefazeromesmoé:first_char=sands[0]

já que and retorna seu segundo valor quando o primeiro é “verdadeiro”, ou oprimeiro valor quando não é. Da mesma forma, se x é um número ou,

Page 54: Data Science do zero: Primeiras regras com o Python

possivelmente,None:safe_x=xor0

definitivamenteéumnúmero.

Python possui uma função all, que pega uma lista e retorna True precisamentequandotodososelementosforemverdadeiros,eumafunçãoany,queretornaTruequandopelomenosumelementoéverdadeiro:

all([True,1,{3}])#Trueall([True,1,{}])#False,{}éfalsoany([True,1,{}])#True,Trueéverdadeiroall([])#True,semelementosfalsosnalistaany([])#False,semelementosverdadeirosnalista

Page 55: Data Science do zero: Primeiras regras com o Python

NãoTãoBásicoAqui, veremos alguns dosmais avançados recursos doPython que serão úteisparatrabalharcomdados.

OrdenaçãoTodalistadePythonpossuiummétodosortqueordenaseuespaço.Sevocênãoquerbagunçarsualista,vocêpodeusarafunçãosorted,queretornamumalistanova:

x=[4,1,2,3]y=sorted(x)#é[1,2,3,4],xnãomudoux.sort()#agoraxé[1,2,3,4]

Porpadrão,sort(esorted)organizamumalistadamenorparaamaiorbaseadaemumacomparaçãoingênuadeelementosunscomosoutros.

Sevocêquerqueoselementossejamorganizadosdomaiorparaomenor,vocêpode especificar o parâmetro reverse=True. E, em vez de comparar os elementoscom elesmesmos, compare os resultados da função que você especificar comkey:

#organizaalistapelovalorabsolutodomaiorparaomenorx=sorted([-4,1,-2,3],key=abs,reverse=True)#is[-4,3,-2,1]

#organizaaspalavrasecontagensdamaisaltaparaamaisbaixawc=sorted(word_counts.items(),

key=lambda(word,count):count,reverse=True)

CompreensõesdeListaCom frequência, você vai querer transformar uma lista em outra, escolhendoapenas alguns elementos, transformando tais elementos ou ambos. O modoPythonicdefazerissosãoascompreensõesdelista:

even_numbers=[xforxinrange(5)ifx%2==0]#[0,2,4]squares=[x*xforxinrange(5)]#[0,1,4,9,16]even_squares=[x*xforxineven_numbers]#[0,4,16]

vocêpodetransformardicionáriosemconjuntosdamesmaforma:

Page 56: Data Science do zero: Primeiras regras com o Python

square_dict={x:x*xforxinrange(5)}#{0:0,1:1,2:4,3:9,4:16}square_set={x*xforxin[1,-1]}#{1}

Se você não precisar do valor da lista, é comum usar um sublinhado comovariável:

zeroes=[0for_ineven_numbers]#possuiomesmotamanhodeeven_numbers

Umacompreensãodelistapodeincluirmúltiplosfor:pairs=[(x,y)

forxinrange(10)foryinrange(10)]#100pairs(0,0)(0,1)...(9,8),(9,9)

eosforquevêmdepoispodemusarosresultadosdosprimeiros:increasing_pairs=[(x,y)#somenteparescomx<y,

forxinrange(10)#range(lo,hi)éigualaforyinrange(x+1,10)]#[lo,lo+1,...,hi-1]

Usaremosbastantescompreensõesdelista.

GeradoreseIteradoresUm problema com as listas é que elas podem crescer sem parar facilmente.range(1000000)criaumalistacomummilhãodeelementos.Sevocêapenasprecisalidarcomelesumdecadavez, issopodeserumafonteinfinitadeineficiência(ou esgotamento de memória). Se você precisar de poucos valores, calculartodosseriaumaperdadetempo.

Umgeradoréalgosobreoqualvocêpodeiterar(paranós,geralmenteusandofor) mas cujos valores são produzidos apenas quando necessários(preguiçosamente).

Umaformadecriargeradoresécomfunçõeseooperadoryield:deflazy_range(n):

"""umaversãopreguiçosaderange"""i=0whilei<n:

yieldii+=1

Oloopaseguirconsumiráosvaloresyieldumdecadavezaténãosobrarmaisnenhum:

foriinlazy_range(10):

Page 57: Data Science do zero: Primeiras regras com o Python

do_something_with(i)

(O Python geralmente vem com uma função lazy_range chamada xrange e, emPython3,rangeé,emsi,preguiçoso(lazy).)Issosignificaquevocêpoderiacriarumasequênciainfinita:

defnatural_numbers():"""retorna1,2,3,..."""n=1whileTrue:

yieldnn+=1

emboravocênãodeveriaiterarsobreelasemusaralgumtipodelógicabreak.

Ooutroladodapreguiçaéquevocêsomentepodeiterarcomumgeradorumavez.Sevocêprecisariterarmúltiplasvezes,vocêteráquerecriarumgeradortodasasvezesouusarumalista.

Uma segunda forma de criar geradores é usar compreensões de for dentro deparênteses:

lazy_evens_below_20=(iforiinlazy_range(20)ifi%2==0)

Lembre-sedequecadadictpossuiummétodoitems()queretornaumalistadeseuspares valores-chave. Veremos com mais frequência o método iteritems(), quepreguiçosamente yields (chama) os pares de valor-chave um de cada vezconformeiteramossobreele.

AleatoriedadeConformeaprendemosdatascience,precisaremosgerarnúmerosaleatórioscomumacertafrequência,oquepodeserfeitocomomódulorandom:

importrandom

four_uniform_randoms=[random.random()for_inrange(4)]

#[0.8444218515250481,#random.random()produznúmeros#0.7579544029403025,#uniformementeentre0e1#0.420571580830845,#éafunçãoaleatóriaqueusaremos#0.25891675029296335]#commaisfrequência

Page 58: Data Science do zero: Primeiras regras com o Python

O módulo random de fato produz números pseudoaleatórios (ou seja,determinísticos) baseado emumestado internoquevocêpode configurar comrandom.seedsequiserobterresultadosreproduzíveis:

random.seed(10)#configuraseedpara10printrandom.random()#0.57140259469random.seed(10)#reiniciaseedpara10printrandom.random()#0.57140259469novamente

Àsvezesusaremosrandom.randrange,quelevaumoudoisargumentoseretornaumelementoescolhidoaleatoriamentedorange()correspondente:

random.randrange(10)#escolhealeatoriamentederange(10)=[0,1,...,9]random.randrange(3,6)#escolhealeatoriamentederange(3,6)=[3,4,5]

Existem mais alguns métodos que achamos convenientes em certas ocasiões.random.shufflereordenaoselementosdeumalistaaleatoriamente:

up_to_ten=range(10)random.shuffle(up_to_ten)printup_to_ten#[2,5,1,9,7,3,8,6,4,0](seusresultadospodemserdiferentes)

Sevocêprecisarescolherumelementorandomicamentedeumalista,vocêpodeusarrandom.choice:

my_best_friend=random.choice(["Alice","Bob","Charlie"])#"Bob"forme

E se você precisar escolher aleatoriamente uma amostra dos elementos semsubstituição(porexemplo,semduplicatas),vocêpodeusarrandom.sample:

lottery_numbers=range(60)winning_numbers=random.sample(lottery_numbers,6)#[16,36,10,6,25,9]

Para escolher uma amostra de elementos com substituição (por exemplo,permitindoduplicatas),vocêpodefazermúltiplaschamadaspararandom.choice:

four_with_replacement=[random.choice(range(10))for_inrange(4)]

#[9,4,4,2]

ExpressõesRegularesAs expressões regulares fornecem uma maneira de procurar por texto. Sãoincrivelmente úteis mas um pouco complicadas, tanto que até existem livrossobreelas.Explicaremosmaisdetalhesnaspoucasvezesqueasencontrarmos;

Page 59: Data Science do zero: Primeiras regras com o Python

estessãoalgunsexemplosdecomousá-lasemPython:importre

printall([#todossãoverdadeirosporquenotre.match("a","cat"),#*'cat'nãocomeçacom'a're.search("a","cat"),#*'cat'possuium'a'notre.search("c","dog"),#*'dog'nãopossuium'c'3==len(re.split("[ab]","carbs")),#*divideemaoubpara['c','r','s']"R-D-"==re.sub("[0-9]","-","R2D2")#*substituidígitosportraços])#imprimeTrue

ProgramaçãoOrientadaaObjetoComo muitas linguagens, o Python permite que você defina classes queencapsulamdadoseasfunçõesqueasoperam.Asusaremosalgumasvezesparatornarnossocódigomais limpoesimples.Provavelmenteémaisfácilexplicá-lasaoconstruirumexemplorepletodeanotações.

Imagine que não tivéssemos o set embutido em Python. Portanto, talvezquiséssemoscriarnossaprópriaclasseSet.

Qual comportamento nossa classe deveria ter? Dado um exemplo de Set,deveremossercapazesdeadd(adicionar)itensnele,remove(remover)itensdeleeverificar se ele contains (contém) um determinado valor. Criaremos todos elescomo funções demembro, o que significa que os acessaremos com um pontodepoisdeumobjetoSet:

#porconvenção,damosnomesPascalCaseàsclassesclassSet:

#estassãoasfunçõesdemembro#cadaumapegaumparâmetro“self”(outraconvenção)#queserefereaoobjetosetsendousadoemquestão

def__init__(self,values=None):"""esteéoconstrutor.EleéchamadoquandovocêcriaumnovoSet.Vocêdeveriausá-locomos1=Set()#conjuntovazios2=Set([1,2,2,3])#inicializacomvalores"""

self.dict={}#cadainstânciadesetpossuisuaprópriapropriedadedict#queéoqueusaremospararastrearasassociações

ifvaluesisnotNone:forvalueinvalues:

Page 60: Data Science do zero: Primeiras regras com o Python

self.add(value)

def__repr__(self):"""estaéarepresentaçãodastringdeumobjetoSetsevocêdigitá-lanopromptdoPythonoupassá-laparastr()"""return"Set:"+str(self.dict.keys())

#representaremosaassociaçãocomoumachaveemself.dictcomvalorTruedefadd(self,value):

self.dict[value]=True

#valorestánoSetseeleforumachavenodicionáriodefcontains(self,value):

returnvalueinself.dict

defremove(self,value):delself.dict[value]

Quepoderíamosusardestaforma:s=Set([1,2,3])s.add(4)prints.contains(4)#Trues.remove(3)prints.contains(3)#False

FerramentasFuncionaisAopassarasfunções,algumasvezesqueremosaplicá-lasparcialmenteparacriarfunções novas.Emum simples exemplo, imagine que temosuma função comduasvariáveis:

defexp(base,power):returnbase**power

equeremosusá-laparacriarumafunçãodeumavariáveltwo_to_thecujaentradaéumpowerecujasaídaéoresultadodeexp(2,power).

Podemos,éclaro,fazerissocomdef,maspodeserumpoucocomplicado:deftwo_to_the(power):

returnexp(2,power)

Umaabordagemdiferenteéusarfunctools.partial:fromfunctoolsimportpartialtwo_to_the=partial(exp,2)#agoraéumafunçãodeumavariávelprinttwo_to_the(3)#8

Vocêtambémpodeusarpartialparapreencherosargumentosquevirãodepoisse

Page 61: Data Science do zero: Primeiras regras com o Python

vocêespecificarseusnomes:square_of=partial(exp,power=2)printsquare_of(3)#9

Começaaficarbagunçadoquandovocêadicionaargumentosnomeiodafunção,portantotentaremosevitarisso.

Ocasionalmenteusaremosmap,reduceefilter,quefornecemalternativasfuncionaisparaascompreensõesdelista:

defdouble(x):return2*x

xs=[1,2,3,4]twice_xs=[double(x)forxinxs]#[2,4,6,8]twice_xs=map(double,xs)#igualaodecimalist_doubler=partial(map,double)#funçãoqueduplicaalistatwice_xs=list_doubler(xs)#novamente[2,4,6,8]

Vocêpodeusarmapcomfunçõesdemúltiplosargumentossefornecermúltiplaslistas:

defmultiply(x,y):returnx*y

products=map(multiply,[1,2],[4,5])#[1*4,2*5]=[4,10]

Igualmente,filterfazotrabalhodeumacompreensãodelistaif:defis_even(x):

"""Truesexforpar,Falsesexforímpar"""returnx%2==0

x_evens=[xforxinxsifis_even(x)]#[2,4]x_evens=filter(is_even,xs)#igualaodecimalist_evener=partial(filter,is_even)#funçãoquefiltraalistax_evens=list_evener(xs)#novamente[2,4]

reduce combina os dois primeiros elementos de uma lista, então esse resultadocomoterceiroeesseresultadocomoquarto;eassimpordiante,produzindoumúnicoresultado:

x_product=reduce(multiply,xs)#=1*2*3*4=24list_product=partial(reduce,multiply)#funçãoquereduzumalistax_product=list_product(xs)#novamente=24

Enumeração(enumerate)Com alguma frequência, você vai querer iterar por uma lista e usar seus

Page 62: Data Science do zero: Primeiras regras com o Python

elementoseseusíndices:#nãoéPythonicforiinrange(len(documents)):

document=documents[i]do_something(i,document)

#tambémnãoéPythonici=0fordocumentindocuments:

do_something(i,document)i+=1

AsoluçãoPythonicéenumerate(enumerar),queproduztuplas(index,element):fori,documentinenumerate(documents):

do_something(i,document)

Damesmaforma,seapenasquisermososíndices:foriinrange(len(documents)):do_something(i)#nãoéPythonicfori,_inenumerate(documents):do_something(i)#Pythonic

Usaremosissobastante.

DescompactaçãodeZipeArgumentosCom uma certa frequência, precisaremos zip (compactar) duas ou mais listasjuntas.ziptransformalistasmúltiplasemumaúnicalistadetuplasdeelementoscorrespondentes:

list1=['a','b','c']list2=[1,2,3]zip(list1,list2)#é[('a',1),('b',2),('c',3)]

Seaslistassãodetamanhosdiferentes,zipparaassimqueaprimeiralistaacaba.

Vocêtambémpodedescompactarumalistausandoumtruquecurioso:pairs=[('a',1),('b',2),('c',3)]letters,numbers=zip(*pairs)

Oasteriscodesempenhaadescompactaçãodeargumento,queusaoselementosdepairscomoargumentosindividuaisparazip.Dánomesmosevocêchamasse:

zip(('a',1),('b',2),('c',3))

queretorna[(‘a’,’b’,’c’),(‘1’,’2’,’3’)].

Vocêpodeusaradescompactaçãodeargumentocomqualquerfunção:

Page 63: Data Science do zero: Primeiras regras com o Python

defadd(a,b):returna+b

add(1,2)#retorna3add([1,2])#TypeError!add(*[1,2])#retorna3

Éraroacharmosissoútil,masquandofazemoséumtruqueengenhoso.

argsekwargsDigamosquequeremoscriarumafunçãodeordemaltaque temcomoentradaumafunçãoferetornaumafunçãonovaqueretornaduasvezesovalordefparaqualquerentrada:

defdoubler(f):defg(x):

return2*f(x)returng

Istofuncionaemalgunscasos:deff1(x):

returnx+1

g=doubler(f1)printg(3)#8(==(3+1)*2)printg(-1)#0(==(-1+1)*2)

Noentanto,elefalhacomfunçõesquepossuemmaisdeumúnicoargumento:deff2(x,y):

returnx+y

g=doubler(f2)printg(1,2)#TypeError:g()pegaexatamente1argumento(2dados)

O que precisamos é de alguma maneira de especificar uma função que levaargumentosarbitrários.Podemosfazerissocomadescompactaçãodeargumentoeumpoucodemágica:

defmagic(*args,**kwargs):print"unnamedargs:",argsprint"keywordargs:",kwargs

magic(1,2,key="word",key2="word2")

#imprime#unnamedargs:(1,2)#keywordargs:{'key2':'word2','key':'word'}

Ou seja, quando definimos uma função como essa, args é uma tupla dos seus

Page 64: Data Science do zero: Primeiras regras com o Python

argumentos sem nome e kwargs é um dict dos seus argumentos com nome.Funcionadaformacontráriatambém,sevocêquiserusarumalist(outuple)edictparafornecerargumentosparaumafunção:

defother_way_magic(x,y,z):returnx+y+z

x_y_list=[1,2]z_dict={"z":3}printother_way_magic(*x_y_list,**z_dict)#6

Você poderia fazer todos os tipos de truques com isso; apenas usaremos paraproduziroutrasfunçõesdeordemaltacujasentradaspodemaceitarargumentosarbitrários:

defdoubler_correct(f):"""funcionanãoimportaquetipodeentradasfespera"""defg(*args,**kwargs):

"""quaisquerargumentoscomosquaisgéfornecido,ospassaparaf"""return2*f(*args,**kwargs)

returng

g=doubler_correct(f2)printg(1,2)#6

Bem-vindoàDataSciencester!Concluímosaquiotreinamentodosfuncionáriosnovos.Ah,etambém,tentenãosurrupiarnada.

Page 65: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosNãoháescassezdetutoriaisdePythonnomundo.Ositeoficial(https://docs.python.org/2/tutorial/)nãoéumlugarruimparacomeçar.OtutorialoficialIPython(http://ipython.org/ipython-doc/2/interactive/tutorial.html)nãoétãobom.Vocêtalvezprefiraassistiraosvídeoseàsapresentações(http://ipython.org/videos.html).Comoalternativa,PythonforDataAnalysis(O'Reilly)doWesMcKinneypossuiumótimocapítulosobreIPython.

Page 66: Data Science do zero: Primeiras regras com o Python

••

CAPÍTULO3

VisualizandoDados

Acreditoqueavisualizaçãosejaumdosmeiosmaispoderososdeatingirmetaspessoais.—HarveyMackay

Uma parte fundamental do kit de ferramentas do cientista de dados é avisualizaçãodedados.Emborasejamuitofácilcriarvisualizações,ébemmaisdifícilproduziralgumasboas.

Existemdoisusosprimáriosparaavisualizaçãodedados:

ParaexplorardadosParacomunicardados

Neste capítulo, nos concentraremos em construir habilidades das quais vocêprecisará para começar a explorar seus próprios dados e produzir asvisualizações que usaremos no decorrer do livro. Como amaioria dos nossostópicos do capítulo, a visualização de dados é uma rica área de estudos quemereceseuprópriolivro.Mas,mesmoassim,tentaremosmostraroqueéprecisoeoquenãoéparaumaboavisualização.

Page 67: Data Science do zero: Primeiras regras com o Python

matplotlibExisteumagrandevariedadedeferramentasparavisualizardados.Usaremosabibliotecamatplotlib(http://matplotlib.org/),queéaltamenteusada(apesardesuaidade). Se você estiver interessado em produzir elaboradas visualizaçõesinterativas para a web, provavelmente não será a melhor escolha mas, parasimplesgráficosdebarras,delinhasededispersão,funcionamuitobem.

Na verdade, usaremos o módulo matplotlib.pyplot. Em seu simples uso, pyplotmantémumestadointernoemquevocêconstróiumavisualizaçãopassoapasso.Aoterminar,vocêpodesalvá-la(comsavefig())ouexibi-la(comshow()).

Porexemplo,fazerumgráficosimples(comoaFigura3-1)ébemfácil:frommatplotlibimportpyplotasplt

years=[1950,1960,1970,1980,1990,2000,2010]gdp=[300.2,543.3,1075.9,2862.5,5979.6,10289.7,14958.3]

#criaumgráficodelinha,anosnoeixox,gdpnoeixoyplt.plot(years,gdp,color='green',marker='o',linestyle='solid')

#adicionaumtítuloplt.title("GDPNominal")

#adicionaumselonoeixoyplt.ylabel("Bilhõesde$")plt.show()

Page 68: Data Science do zero: Primeiras regras com o Python

Figura3-1.Umgráficodelinhasimples

Construir gráficos que possuam uma boa qualidade de imagem é maiscomplicadoeestáalémdoescopodestecapítulo.Existemdiversasmaneirasdepersonalizarseusgráficoscomrótulosdeeixos,estilosdelinhaemarcadoresdeponto,porexemplo.Emvezdepassarporumtratamentodecompreensãocomessas opções, apenas usaremos (e chamaremos à atenção) alguns deles emnossosexemplos.

Embora não usemos muito dessa funcionalidade, matplotlib é capaz de produzirgráficos complicados dentro de gráficos, formatação sofisticada e visualizaçõesinterativas. Verifique sua documentação caso queira se aprofundar mais do queapresentamosnestelivro.

Page 69: Data Science do zero: Primeiras regras com o Python

GráficosdeBarraUmgráficodebarraéumaboaescolhaquandovocêquermostrarcomoalgumasquantidadesvariamentreumconjuntoparticulardeitens.Porexemplo,aFigura3-2mostra quantas premiações doOscar cada uma das variedades dos filmesganharam:

movies=["AnnieHall","Ben-Hur","Casablanca","Gandhi","WestSideStory"]num_oscars=[5,11,3,8,10]

#barraspossuemotamanhopadrãode0.8,entãoadicionaremos0.1às#coordenadasàesquerdaparaquecadabarrasejacentralizadaxs=[i+0.1fori,_inenumerate(movies)]

#asbarrasdográficocomascoordenadasxàesquerda[xs],alturas[num_oscars]plt.bar(xs,num_oscars)

plt.ylabel("#dePremiações")plt.title("MeusFilmesFavoritos")

#nomeiaoeixoxcomnomesdefilmesnabarracentralplt.xticks([i+0.5fori,_inenumerate(movies)],movies)

plt.show()

Page 70: Data Science do zero: Primeiras regras com o Python

Figura3-2.Umgráficodebarrasimples

Umgráfico de barra tambémpode ser umaboa escolha para criar gráficos dehistogramas de valores numéricos carregados, a fim de explorar visualmentecomoosvaloressãodistribuídos,comonaFigura3-3:

grades=[83,95,91,87,70,0,85,82,100,67,73,77,0]decile=lambdagrade:grade//10*10histogram=Counter(decile(grade)forgradeingrades)

plt.bar([x-4forxinhistogram.keys()],#movecadabarraparaaesquerdaem4histogram.values(),#dáparacadabarrasuaalturacorreta8)#dáparacadabarraalargurade8

plt.axis([-5,105,0,5])#eixoxde–5até105,#eixoyde0até5

plt.xticks([10*iforiinrange(11)])#rótulosdoeixoxem0,10,…,100plt.xlabel("Decil")plt.ylabel("#deAlunos")plt.title("DistribuiçãodasNotasdoTeste1")plt.show()

Page 71: Data Science do zero: Primeiras regras com o Python

Figura3-3.Usandoumgráficodebarraparaumhistograma

Oterceiroargumentoparaplt.barespecificaalarguradabarra.Aqui,escolhemosa largura 8 (o que deixa um espaço pequeno entre as barras, já que nossoagrupamentopossuiotamanho10).Andamoscomabarraem4,paraque(porexemplo)abarra“80”tenhaseuladoesquerdoedireitoem76e84,e(portanto)seucentroem80.

Achamadaparaplt.axisindicaquequeremosqueoeixoxvarieentre–5até105(paraqueasbarras“0”e“100”sejammostradasporcompleto),equeoeixoydeveriavariarde0até5.Achamadaparaplt.xtickscolocaosrótulosdoeixoxem0,10,20,…,100.

Seja criteriosoquandousar plt.axis().Ao criar gráficos de barra, não começar oeixoyem0éconsideradoruim,jáqueessaéumamaneirafácildeenganaraspessoas(Figura3-4):

mentions=[500,505]

Page 72: Data Science do zero: Primeiras regras com o Python

years=[2013,2014]

plt.bar([2012.6,2013.6],mentions,0.8)plt.xticks(years)plt.ylabel("#devezesqueouvimosalguémdizer'datascience'")

#sevocênãofizerisso,matplotlibnomearáoeixoxde0,1

#eentãoadicionaa+2.013e3paraforadocanto(matplotlibfeio!)plt.ticklabel_format(useOffset=False)

#enganaroeixoymostraapenasaparteacimade500plt.axis([2012.5,2014.5,499,506])plt.title("Olheo"Grande"Aumento!")plt.show()

Figura3-4.Umgráficocomumeixoyenganador

Na Figura 3-5, usamos eixos mais sensatos e, agora, parece menosimpressionante:

plt.axis([2012.5,2014.5,0,550])plt.title("NãoTãoGrandeAgora")plt.show()

Page 73: Data Science do zero: Primeiras regras com o Python

Figura3-5.Omesmográficosemumeixoyenganador

Page 74: Data Science do zero: Primeiras regras com o Python

GráficosdeLinhasComojáhavíamosdito,podemosconstruirgráficosdelinhausandoplt.plot().Elessãoumaboaescolhaaomostrartendências,comoaFigura3-6ilustra:

variance=[1,2,4,8,16,32,64,128,256]bias_squared=[256,128,64,32,16,8,4,2,1]total_error=[x+yforx,yinzip(variance,bias_squared)]xs=[ifori,_inenumerate(variance)]

#podemosfazermúltiplaschamadasparaplt.plot#paramostrarmúltiplassériesnomesmográficoplt.plot(xs,variance,'g-',label='variance')#linhaverdesólidaplt.plot(xs,bias_squared,'r-.',label='bias^2')#linhacomlinhadepontotracejadovermelhoplt.plot(xs,total_error,'b:',label='totalerror')#linhacompontilhadoazul

#porqueatribuímosrótulosparacadasérie#podemosobterumalegendagratuita#loc=9significa“topcenter”plt.legend(loc=9)plt.xlabel("complexidadedomodelo")plt.title("CompromissoentrePolarizaçãoeVariância")plt.show()

Page 75: Data Science do zero: Primeiras regras com o Python

Figura3-6.Váriosgráficosdelinhacomumalegenda

Page 76: Data Science do zero: Primeiras regras com o Python

GráficosdeDispersãoUmgráficodedispersãoéaescolhacertaparavisualizarorelacionamentoentredois pares de conjuntos de dados. Por exemplo, a Figura 3-7 ilustra orelacionamentoentreonúmerodeamigosqueseususuáriostêmeonúmerodeminutosqueelespassamnositepordia:

friends=[70,65,72,63,71,64,60,64,67]minutes=[175,170,205,120,220,130,105,145,190]labels=['a','b','c','d','e','f','g','h','i']

plt.scatter(friends,minutes)

#nomeiacadaposiçãoforlabel,friend_count,minute_countinzip(labels,friends,minutes):

plt.annotate(label,xy=(friend_count,minute_count),#colocaorótulocomsuaposiçãoxytext=(5,-5),#mascompensaumpoucotextcoords='offsetpoints')

plt.title("MinutosDiáriosvs.NúmerodeAmigos")

plt.xlabel("#deamigos")plt.ylabel("minutosdiáriospassadosnosite")plt.show()

Page 77: Data Science do zero: Primeiras regras com o Python

Figura3-7.Umadispersãodeamigosetemponosite

Se você está espalhando variáveis comparáveis, talvez você obtenha umaimagemenganosasedeixarmatplotlibescolheraescala,comonaFigura3-8:

test_1_grades=[99,90,85,97,80]test_2_grades=[100,85,60,90,70]

plt.scatter(test_1_grades,test_2_grades)plt.title("Oseixosnãosãocompatíveis")plt.xlabel("notadoteste2")plt.ylabel("notadoteste1")plt.show()

Page 78: Data Science do zero: Primeiras regras com o Python

Figura3-8.Umgráficodedispersãocomeixosincompatíveis

Se incluirmos uma chamada para plt.axis(“equals”), o gráfico (Figura 3-9) maisprecisomostraqueamaiorpartedavariaçãoacontecenoteste2.

É o suficiente para você começar a criar visualizações. Aprenderemos muitomaissobrevisualizaçãonodecorrerdolivro.

Page 79: Data Science do zero: Primeiras regras com o Python

Figura3-9.Omesmográficodedispersãocomeixosiguais

Page 80: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentos

seaborn(http://stanford.io/1ycOjdI)éconstruídonotopodematplotlibepermitequevocêproduzafacilmentevisualizaçõesmaisbonitas(emaiscomplexas).D3.js(http://d3js.org)éumabibliotecaJavaScriptqueproduzvisualizaçõesinterativassofisticadasparaaweb.EmboranãoestejaemPython,éumatendênciaeamplamenteusada,evaleapenasefamiliarizarcomela.Bokeh(http://bokeh.pydata.org)éabibliotecamaisnovaquetrazoestilodevisualizaçãoD3paraPython.ggplot(http://bit.ly/1ycOk1u)éumaportagemPythondapopularbibliotecaggplot2,queéamplamenteusadaparacriargráficosediagramasde“qualidadedepublicação”.Provavelmente,émaisinteressantesevocêjáéumusuáriovorazdeggplot2eumpoucoopacosenãoé.

Page 81: Data Science do zero: Primeiras regras com o Python

CAPÍTULO4

ÁlgebraLinear

ExistealgomaisinútiloumenosútilqueÁlgebra?—BillyConnolly

A Álgebra Linear é o ramo da matemática que lida com espaços vetoriais.Apesardeeunãoacharquevouconseguirensinarálgebralinearemumcapítulo,ela sustenta umgrandenúmerode conceitos e técnicas de data science, o quesignifica que eu devo a você, ao menos, uma tentativa. O que aprenderemosnestecapítulo,usaremosexcessivamentenodecorrerdolivro.

Page 82: Data Science do zero: Primeiras regras com o Python

VetoresAbstratamente, os vetores são objetos que podem ser somados juntos (paraformar vetores novos) e que podem ser multiplicados pelos escalares (porexemplo,números),tambémparaformarvetoresnovos.

Concretamente(paranós),osvetoressãopontosemalgumespaçodedimensãofinita.Apesar de você não pensar em seus dados como vetores, eles são umaótimamaneiraderepresentardadosnuméricos.

Porexemplo,sevocêtiverasalturas,pesoseidadesdeumagrandequantidadede pessoas, pode tratar seus dados como vetores tridimensionais (height,weight,age).Sevocêestiverensinandoumaturmacomquatrotestes,podetratarasnotasdosalunoscomovetoresquadridimensionais(exam1,exam2,exam3,exam4).

Aabordageminicialmaissimplesérepresentarvetorescomolistasdenúmeros.Umalistadetrêsnúmeroscorrespondeaumvetoremumespaçotridimensional,evice-versa:

height_weight_age=[70,#polegadas,170,#quilos,40]#anos

grades=[95,#teste180,#teste275,#teste362]#teste4

Um problema com essa abordagem é que queremos realizar aritmética nosvetores. Como as listas de Python não são vetores (e, portanto, não facilita aaritméticacomovetor),precisaremosconstruiressasferramentasaritméticasnósmesmos.Então,vamoscomeçarporaí.

Para começar, frequentemente precisaremos de dois vetores. Os vetores seadicionamcomponenteacomponente. Isso significaque, sedoisvetoresv ewpossuemomesmotamanho,suasomaésomenteovetorcujoprimeiroelementosejav[0]+w[0],cujosegundoelementosejav[1]+w[1],eassimpordiante.(Seelesnãopossuíremomesmotamanho,entãonãopoderemossomá-los.)

Porexemplo,somarosvetores[1,2]e[2,1]resultaem[1+2,2+1]ou [3,3],como

Page 83: Data Science do zero: Primeiras regras com o Python

mostraaFigura4-1.

Figura4-1.Somandodoisvetores

Podemos facilmente implementar isso com vetores zip juntos e usar umacompreensãodelistaparaadicionaroselementoscorrespondentes:

defvector_add(v,w):"""somaelementoscorrespondentes"""return[v_i+w_i

forv_i,w_iinzip(v,w)]

Da mesma forma, para subtrair dois vetores, apenas subtraia os elementoscorrespondentes:

defvector_subtract(v,w):"""subtraielementoscorrespondentes"""return[v_i-w_i

forv_i,w_iinzip(v,w)]

Às vezes queremos somar uma lista de vetores.Ou seja, criar um vetor novocujo primeiro elemento seja a soma de todos os primeiros elementos, cujo

Page 84: Data Science do zero: Primeiras regras com o Python

segundo elemento seja a soma de todos os segundos elementos, e assim pordiante.Amaneiramaisfácildefazerissoéadicionarumvetordecadavez:

defvector_sum(vectors):"""somatodaoselementoscorrespondentes"""result=vectors[0]#começacomoprimeirovetorforvectorinvectors[1:]:#depoispassaportodososoutros

result=vector_add(result,vector)#eosadicionaaoresultadoreturnresult

Sevocêpensararespeito,estamosapenasreduzindo(reducing)alistadevetoresusando vector_ add, o que significa que podemos reescrever de forma reduzidausandofunçõesdealtaordem:

defvector_sum(vectors):returnreduce(vector_add,vectors)

ouatémesmo:vector_sum=partial(reduce,vector_add)

emboraesseúltimosejamaisespertodoqueútil.

Tambémprecisaremossercapazesdemultiplicarumvetorporumescalar,quesimplesmentefazemosaomultiplicarcadaelementodovetorporaquelenúmero:

defscalar_multiply(c,v):"""céumnúmero,véumvetor"""return[c*v_iforv_iinv]

Isso permite que computemos a média de uma lista de vetores (do mesmotamanho):

defvector_mean(vectors):"""computarovetorcujoi-ésimoelementosejaamédiadosi-ésimoselementosdosvetoresinclusos"""n=len(vectors)returnscalar_multiply(1/n,vector_sum(vectors))

Uma ferramenta menos óbvia é o produto escalar (dot product). O produtoescalardedoisvetoreséasomadeseusprodutoscomponenteacomponente:

defdot(v,w):"""v_1*w_1+...+v_n*w_n"""returnsum(v_i*w_i

forv_i,w_iinzip(v,w))

Page 85: Data Science do zero: Primeiras regras com o Python

Oprodutoescalarmedeadistânciaaqualovetorvseestendenadireçãodew.Porexemplo,sew=[1,0]entãodot(v,w)éoprimeirocomponentedev.Outraformadedizerissoéqueesseéotamanhodovetorquevocêteriaseprojetassevemw(Figura4-2).

Figura4-2.Oprodutoescalarcomoprojeçãodevetor

Assim,éfácilcomputarasomadosquadradosdeumvetor:defsum_of_squares(v):

"""v_1*v_1+...+v_n*v_n"""returndot(v,v)

Quepodemosusarparacomputarsuamagnitude(outamanho):importmath

defmagnitude(v):returnmath.sqrt(sum_of_squares(v))#math.sqrtéafunçãodaraizquadrada

Agoratemostodasaspeçasdasquaisprecisamosparacomputaradistânciaentre

Page 86: Data Science do zero: Primeiras regras com o Python

doisvetores,definidacomo:

defsquared_distance(v,w):"""(v_1-w_1)**2+...+(v_n-w_n)**2"""returnsum_of_squares(vector_subtract(v,w))

defdistance(v,w):returnmath.sqrt(squared_distance(v,w))

Queficamaisclaroseescrevermoscomo(oequivalente):defdistance(v,w):

returnmagnitude(vector_subtract(v,w))

Isso deve ser o suficiente para começarmos; usaremos essas funçõesconstantementenodecorrerdolivro.

Usarlistascomovetoresébomparaaexposição,masterrívelparaodesempenho.

Naproduçãodecódigo,vocêpodequererusarabibliotecaNumPy,queincluiumaclasse de array de alta performance com todos os tipos de operaçõesmatemáticasinclusas.

Page 87: Data Science do zero: Primeiras regras com o Python

MatrizesUma matriz é uma coleção de números bidimensional. Representaremos asmatrizescomolistasdelistas,comcadalistainteriorpossuindoomesmotamanhoerepresentandoumalinhadamatriz.SeAéumamatriz,logoA[i][j]éoelementoda i-ésima linha e j-ésima da coluna. Por convenção matemática, geralmenteusaremosletrasmaiúsculaspararepresentarmatrizes.Porexemplo:

A=[[1,2,3],#Apossuiduaslinhasetrêscolunas[4,5,6]]

B=[[1,2],#Bpossuitrêslinhaseduascolunas[3,4],[5,6]]

Namatemática,normalmentenomearíamosaprimeiralinhadamatrizde“linha1”eaprimeiracolunade“coluna1”.JáqueestamosrepresentandomatrizescomaslistasdePython,quesãoindexadasemzero,chamaremosaprimeiralinhadeumamatrizde“linha0”eaprimeiracolunade“coluna0”.

Dada esta representação de lista-das-listas, amatrizA possui as linhas len(A) ecolunaslen(A[0]),queconsideramosdestaforma(shape):

defshape(A):num_rows=len(A)num_cols=len(A[0])ifAelse0#númerodeelementosnaprimeiralinhareturnnum_rows,num_cols

Se uma matriz possui n linhas e k colunas, nos referiremos a ela como umamatriznxk.Podemos(e,àsvezes,iremos)pensaremcadalinhadeumamatriznxkcomoumvetordetamanhok,ecadacolunacomoumvetordetamanhon:

defget_row(A,i):returnA[i]#A[i]jáédalinhaA[i]élinhai-ésimo

defget_column(A,j):return[A_i[j]#j-ésimoelementodalinhaA_i

forA_iinA]#paracadalinhaA_i

Também queremos saber como criar matrizes dadas sua forma e uma funçãoparaproduzirseuselementos.Podemosfazerissousandoumacompreensãode

Page 88: Data Science do zero: Primeiras regras com o Python

listaaninhada:defmake_matrix(num_rows,num_cols,entry_fn):

"""retornaamatriznum_rowsXnum_colscujaentrada(i,j)théentry_fn(i,j)"""return[[entry_fn(i,j)#dadoi,criaumalista

forjinrange(num_cols)]#[entry_fn(i,0),...]foriinrange(num_rows)]#criaumalistaparacadai

Dadaestafunção,vocêpoderiafazerumamatrizdeidentidade5×5(com1snadiagonale0snosdemaislugares)com:

defis_diagonal(i,j):"""1'snadiagonal,0'snosdemaislugares"""return1ifi==jelse0

#[[1,0,0,0,0],#[0,1,0,0,0],#[0,0,1,0,0],#[0,0,0,1,0],#[0,0,0,0,1]]

Asmatrizesserãoimportantesparanósdediversasformas.

Primeiro, podemos usar uma matriz para representar um conjunto de dadosconsistindo demúltiplos vetores, simplesmente considerando cada vetor comoumalinhadamatriz.Porexemplo,sevocêtivesseaaltura,opesoeaidadede1000pessoas,vocêpoderiacolocá-losemumamatriz1000×3:

data=[[70,170,40],[65,120,26],[77,250,19],#....]

Segundo, como veremos mais tarde, podemos usar uma matriz n × k pararepresentar uma função linear quemapeia vetores dimensionaisk para vetoresdimensionaisn.Nossasváriastécnicaseconceitosenglobarãotaisfunções.

Terceiro, asmatrizes podem ser usadas para representar relações binárias. NoCapítulo1, representamosasextremidadesdeuma redecomoumacoleçãodepares(i, j).UmarepresentaçãoalternativaseriacriarumamatrizA talqueA[i][j]seja1seosnodosiejestejamconectadose0deoutraforma.

Page 89: Data Science do zero: Primeiras regras com o Python

Lembre-sedequetínhamosantes:friendships=[(0,1),(0,2),(1,2),(1,3),(2,3),(3,4),

(4,5),(5,6),(5,7),(6,8),(7,8),(8,9)]

Tambémpoderíamosrepresentardestaforma:#usuário0123456789#

friendships=[[0,1,1,0,0,0,0,0,0,0],#user0[1,0,1,1,0,0,0,0,0,0],#user1[1,1,0,1,0,0,0,0,0,0],#user2[0,1,1,0,1,0,0,0,0,0],#user3[0,0,0,1,0,1,0,0,0,0],#user4[0,0,0,0,1,0,1,1,0,0],#user5[0,0,0,0,0,1,0,0,1,0],#user6[0,0,0,0,0,1,0,0,1,0],#user7[0,0,0,0,0,0,1,1,0,1],#user8[0,0,0,0,0,0,0,0,1,0]]#user9

Se tivermos poucas conexões, essa será uma representaçãomuto fraca já quevocêteráquecompletaroarmazenamentocomdiversoszeros.Noentanto,coma representação da matriz é bem mais fácil verificar se os dois nodos estãoconectados — você apenas tem que procurar na matriz em vez de procurar(possivelmente)cadaextremidade:

friendships[0][2]==1#True,0e2sãoamigosfriendships[0][8]==1#False,0e8nãosãoamigos

Damesmaforma,paraencontrarasconexõesqueumnodopossui,vocêprecisaapenasinspecionaracoluna(oualinha)correspondenteàquelenodo:

friends_of_five=[i#somenteprecisamosfori,is_friendinenumerate(friendships[5])#olharparaifis_friend]#umalinha

Anteriormente, adicionamos uma lista de conexões para cada objeto de nodopara aumentar sua velocidade de processo,mas para um gráfico grande e emevolução,seriamuitocaroededifícilmanutenção.

Veremosmatrizesnovamentenodecorrerdolivro.

Page 90: Data Science do zero: Primeiras regras com o Python

———

ParaMaisEsclarecimentosAálgebralinearéamplamenteusadaporcientistasdedados(implícitacomfrequência,enãoraramenteporpessoasquenãoaentendem).Nãoseriaumamáideialerumlivrodidático.Vocêpodeencontrarváriosdisponíveisonline:

LinearAlgebra,daUCDavis(http://bit.ly/1ycOq96)LinearAlgebra,doSaintMichael’sCollege(http://bit.ly/1ycOpSF)Se gostar de aventuras, Linear Algebra Done Wrong(http://bit.ly/1ycOt4W)éumlivrocomumaintroduçãomaisavançada

TodososexemplosconstruídosaquivocêpodetergratuitamenteseusarNumPy(http://www.numpy.org).(Emaisexemplostambém.)

Page 91: Data Science do zero: Primeiras regras com o Python

CAPÍTULO5

Estatística

Osfatossãoteimosos,masasestatísticassãomaismaleáveis.—MarkTwain

Aestatística se refereàmatemáticaeàs técnicascomasquaisentendemososdados.Éumcampo rico,amplo,maisadequadoaumaprateleira (ou sala)emumabibliotecaemvezdeumcapítuloemumlivro,portanto,nossaabordagemnãoserámuitoprofunda.Emvezdisso,tentareiexplicarapenasosuficienteparaensinaraseraventureiroecaptarseuinteresseparaquevocêdêcontinuidadeeaprendamais.

Page 92: Data Science do zero: Primeiras regras com o Python

DescrevendoumConjuntoÚnicodeDadosPor meio de uma combinação de discurso oral e sorte, a DataSciencesterampliouparadúziasdemembroseovice-presidentedaCaptaçãodeRecursossolicitaumrelatóriodequantosamigosseusmembrospossuemafimdeincluí-losemseusdiscursosnoelevador.

AousarastécnicasdoCapítulo1,vocêéplenamentecapazdeproduzirdados.Mas,agora,vocêestádiantedoproblemadecomodescrevê-los.

Umadescriçãoevidentedequalquerdadoésimplesmenteodadoemsi:num_friends=[100,49,41,40,25,

#…emuitosmais]

Paraumconjuntopequenodedados,essapodeatéseramelhorrepresentação.Mas,paraumconjuntomaior,elaécomplicadaeconfusa.(Imagineolharparaumalistadeummilhãodenúmeros.)Poressa razão,usamosaestatísticaparadestilarecomunicarosaspectosrelevantesdosnossosdados.

Na primeira abordagem, colocamos a contagemde amigos emumhistogramausandoCountereplt.bar()(Figura5-1):

friend_counts=Counter(num_friends)xs=range(101)#ovalormaioré100ys=[friend_counts[x]forxinxs]#aalturaésomente#deamigosplt.bar(xs,ys)plt.axis([0,101,0,25])plt.title("HistogramadaContagemdeAmigos")plt.xlabel("#deamigos")plt.ylabel("#depessoas")plt.show()

Page 93: Data Science do zero: Primeiras regras com o Python

Figura5-1.Umhistogramadacontagemdeamigos

Infelizmente, esse gráfico ainda é muito difícil para inserir em discussões.Portanto, é melhor começar a gerar algumas estatísticas. Provavelmente, aestatísticamaissimpleséonúmerodepontosnosdados:

num_points=len(num_friends)#204

Possivelmente,vocêtambémestáinteressadonosmaioresemenoresvalores:largest_value=max(num_friends)#100smallest_value=min(num_friends)#1

que são apenas casos especiais de querer saber os valores em posiçõesespecíficas:

sorted_values=sorted(num_friends)smallest_value=sorted_values[0]#1second_smallest_value=sorted_values[1]#1second_largest_value=sorted_values[-2]#49

Page 94: Data Science do zero: Primeiras regras com o Python

Masestamosapenascomeçando.

TendênciasCentraisGeralmente,queremosteralgumanoçãodeondenossosdadosestãocentrados.A média será mais utilizada pois ela é soma dos dados dividido pela suacontagem:

#nãoestácertosevocênãoimportaradivisãode__future__defmean(x):

returnsum(x)/len(x)

mean(num_friends)#7.333333

Sevocêpossui dois pontos de dados, amédia é o pontonomeiodo caminhoentres eles. Conforme você acrescenta mais pontos, a média se move, massempredependedovalordecadaponto.

Algumasvezes tambémnos interessaremospelamediana,queéovalormaiordomeio(seonúmerodepontosdedadosforímpar)ouamédiadosdoisvaloresqueestiverembemnomeio(seonúmerodepontosdedadosforpar).

Por exemplo, se tivermos cinco pontos de dados em um vetor variado x, amedianaéx[5//2]oux[2].Setivermosseispontosdedados,queremosamédiadex[2](oterceiroponto)ex[3](oquartoponto).

Repareque—diferentedamédia—amediananãodependedecadavalornosseusdados.Porexemplo,sevocêaumentaromaiorponto(oudiminuiromenorponto),ospontosdomeiopermanecemintactos,logo,amedianatambém.

Afunçãomedianéumpoucomaiscomplicadadoquevocêpensa,principalmenteporcausadocaso“par”:

defmedian(v):"""encontraovalormaisaomeiodev"""n=len(v)sorted_v=sorted(v)midpoint=n//2

ifn%2==1:#seforímpar,retornaovalordomeioreturnsorted_v[midpoint]

else:

Page 95: Data Science do zero: Primeiras regras com o Python

#seforpar,retornaamédiadosvaloresdomeiolo=midpoint-1hi=midpointreturn(sorted_v[lo]+sorted_v[hi])/2

median(num_friends)#6.0

Evidentemente, amédiaémais fácildecomputarevariademodomais suaveconformeosdadosmudam.Se tivermosn pontosdedadoseumdelescresceremumaquantidadee,logo,necessariamente,amédiaaumentarádee/n.(Issofazdamédiaaresponsávelportodosostruquesdecálculo.)Mas,paraencontraramediana, temosqueorganizar nossos dados.E, aomudar umdos pontos dedadosemumapequenaquantidadee,talvezamedianaaumentedee,oualgumnúmeromenorquee,ounenhumdeles(dependendodonúmerodedados).

Há, na verdade, alguns truques não tão óbvios para computar medianas(http://en.wikipedia.org/wiki/Quickselect) sem sem organizar os dados.No entanto,elesestãoalémdoescopodestelivro,entãotemosqueorganizá-los.

Aomesmotempo,amédiaémuitosensívelaosvaloresdiscrepantesemnossosdados.Senossousuáriomaisamigávelpossuiduzentosamigos(emvezdecem),entãoamédiasubiriapara7.82,enquantoamedianapermaneceriaamesma.Seosvaloresdiscrepantes têmapossibilidadedeseremdados ruins (ou,deoutromodo, não representativos de qualquer fenômeno que estejamos tentandoentender), então amédia pode nos levar a um engano. Por exemplo, conta-seque,emmeadosdadécadade1980,agraduaçãodaUniversidadedaCarolinadoNorte com amaior média de salário inicial era geografia, principalmente porcausadaestreladoNBA(eumvalordiscrepante)MichaelJordan.

Umageneralizaçãodamédiaéoquantil,querepresentaovalorabaixodoqualauma certa porcentagem dos dados se encontra (a mediana representa o valorabaixodoqual50%dosdadosseencontram).

defquantile(x,p):"""retornaovalorpercentualp-ésimoemx"""p_index=int(p*len(x))returnsorted(x)[p_index]

quantile(num_friends,0.10)#1quantile(num_friends,0.25)#3

Page 96: Data Science do zero: Primeiras regras com o Python

quantile(num_friends,0.75)#9quantile(num_friends,0.90)#13

Demodomenos comum, você talvez queira olhar para amoda ou os valoresmaiscomuns:

defmode(x):"""retornaumalista,podehavermaisdeumamoda"""counts=Counter(x)max_count=max(counts.values())return[x_iforx_i,countincounts.iteritems()

ifcount==max_count]

mode(num_friends)#1and6

Masusaremosamédiacommaisfrequência.

DispersãoA dispersão se refere à medida de como os nossos dados estão espalhados.Tipicamente, eles sãoestatísticas emquevalorespertode zero significamnãoestãoespalhadosdeformaalgumaeparavaloresmaiores(oquequerqueissosignifique) significamuito espalhados. Por exemplo, uma simplesmedida é aamplitude,queéadiferençaentreomaioreomenorelemento:

#“amplitude”jápossuisignificadoemPython,entãousaremosumnomediferentedefdata_range(x):

returnmax(x)-min(x)

data_range(num_friends)#99

Aamplitudeézeroquandoomaxeominsãoiguais,oqueaconteceapenasseoselementosdexforemtodosiguais,oquesignificaqueosdadosestãoomenosdispersospossível. Poroutro lado, se a amplitude é ampla, entãoomax é bemmaiordoqueomineosdadosestãomaisespalhados.

Assimcomoamediana,aamplitudenãodependedefatodetodooconjuntodedados.Umconjuntodedadoscujospontosestãotodosentre0ou100possuiamesmaamplitudequeumcujosvaloressão0,100emuitos50s.Masparecequeoprimeiroconjuntodedados“deveria”estarmaisespalhado.

Umamedidadedispersãomaiscomplexaéavariância,computadadestaforma:

Page 97: Data Science do zero: Primeiras regras com o Python

defde_mean(x):"""deslocaxaosubtrairsuamédia(entãooresultadotemamédia0)"""x_bar=mean(x)return[x_i-x_barforx_iinx]

defvariance(x):"""presumequextemaomenosdoiselementos"""n=len(x)deviations=de_mean(x)returnsum_of_squares(deviations)/(n-1)

variance(num_friends)#81.54

Parece que é quase o desvio do quadrado médio da média, exceto que estamosdividindo por n-1 em vez de n. Na verdade, quando com uma amostra de umapopulaçãomaior,x_baréapenasumaestimativadamédiareal,oquesignificaquenamédia (x_i – x_bar)**2 há um subestimado quadrado médio da média de x_i damédia, e é por isso que dividimos por n–1 em vez de n. Veja a Wikipédia emhttp://bit.ly/1L2EapI.

Agora, qualquer que seja a unidade na qual nossos dados estão (por exemplo,“friends”), todas as nossas medidas de tendências centrais estão na mesmaunidade.Aamplitudeestaránaquelamesmaunidade também.Avariância,poroutro lado, possui unidades que são osquadrados das unidades originais (porexemplo, “friends squared”). Como pode ser difícil entender isso, geralmenteolhamosparaodesviopadrão:

defstandard_deviation(x):returnmath.sqrt(variance(x))

standard_deviation(num_friends)#9.03

Tantoaamplitudequantoodesviopadrãopossuemomesmoproblemadevalordiscrepantequevimoscomamédia.Usandoomesmoexemplo,senossousuáriomaisamigáveltivesseduzentosamigos,odesviopadrãoseriade14,89,maisdoque60%amais!

Eumaalternativamaisrobustacomputaadiferençaentreospercentos(quantos)75%e25%dovalor:

definterquartile_range(x):returnquantile(x,0.75)-quantile(x,0.25)

interquartile_range(num_friends)#6

Page 98: Data Science do zero: Primeiras regras com o Python

quenãoéafetadoporumapequenaquantidadedevaloresdiscrepantes.

Page 99: Data Science do zero: Primeiras regras com o Python

CorrelaçãoA vice-presidente de Crescimento na DataSciencester tem uma teoria que aquantidade de tempo gasto pelas pessoas no site é relacionada ao número deamigosqueelaspossuem(elanãoéumavice-presidenteàtoa),eelapediuparavocêverificarisso.

Apósexaminarosregistrosdotráfego,vocêdesenvolveumalistadaily_minutesquemostra quantosminutos por dia cada usuário passa naDataSciencester e vocêhavia ordenado essa lista para que seus elementos correspondessem aoselementosdalistaanteriornum_friends.Gostaríamosdeinvestigararelaçãoentreessasduasmétricas.

Primeiro, investigaremos a covariância, o equivalente pareado da variância.Enquanto a variância mede como uma única variável desvia de sua média, acovariânciamedecomoduasvariáveisvariamemconjuntodesuasmédias:

defcovariance(x,y):n=len(x)returndot(de_mean(x),de_mean(y))/(n-1)

covariance(num_friends,daily_minutes)#22.43

Lembre-se que o dot resume os produtos dos pares correspondentes doselementos.Quandooselementoscorrespondentesdexeyestãoacimaouabaixodesuasmédias,umnúmeropositivoentranasoma.Quandoumestáacimadesuamédiaeooutroestáabaixo,umnúmeronegativoentranasoma.Namesmaproporção,umacovariânciapositiva“grande”significaquextendeasergrandequando y é grande e pequeno quando y é pequeno. Uma covariância negativa“grande”significaooposto—quex tendea serpequenoquandoyégrandeevice-versa.Umacovariânciapertodezerosignificaquetalrelaçãonãoexiste.

Mesmoassim,essenúmeropodeserdifícildeserinterpretadopordoismotivos:

Suasunidadessãooprodutodasunidadesdeentrada(porexemplo,minutosamigo-por-dia),oquepodeserdifícildeentender.(Oqueéum“minutosamigo-por-dia”?)Secadausuáriotiverduasvezesmaisamigos(masomesmonúmerode

Page 100: Data Science do zero: Primeiras regras com o Python

minutos),acovariânciaseriaduasvezesmaior.Mas,poralgummotivo,asvariáveisseriamapenasinter-relacionadas.Vistodeoutramaneira,éarriscadodizeroquecontacomoumacovariância“grande”.

Portaismotivos,émaiscomumconsideraracorrelação,quedivideosdesviospadrõesdasduasvariáveis:

defcorrelation(x,y):stdev_x=standard_deviation(x)stdev_y=standard_deviation(y)ifstdev_x>0andstdev_y>0:

returncovariance(x,y)/stdev_x/stdev_yelse:

return0#senãohouveramplitude,acorrelaçãoézero

correlation(num_friends,daily_minutes)#0.25

A correlation não possui unidade e sempre permanece entre –1 (anticorrelaçãoperfeita) e 1 (correlação perfeita). Um número como 0,25 representa umacorrelaçãopositivarelativamentefraca.

Noentanto,algoqueesquecemosdefazerfoiexaminarnossosdados.DêumaolhadanaFigura5-2.

Page 101: Data Science do zero: Primeiras regras com o Python

Figura5-2.Correlaçãocomumvalordiscrepante

Apessoacom100amigos(quepassaapenasumminutopordianosite)éumgrande valor discrepante e a correlação pode ser muito sensível para valoresdiscrepantes.Oqueaconteceseoignorarmos?

outlier=num_friends.index(100)#índicedovalordiscrepante

num_friends_good=[xfori,xinenumerate(num_friends)ifi!=outlier]

daily_minutes_good=[xfori,xinenumerate(daily_minutes)ifi!=outlier]

correlation(num_friends_good,daily_minutes_good)#0.57

Semovalordiscrepante,háumacorrelaçãobemmaisforte(Figura5-3).

Page 102: Data Science do zero: Primeiras regras com o Python

Figura5-3.Correlaçãoapósaremoçãodovalordiscrepante

Você averígua e descobre que o valor discrepante era, na verdade, uma contateste interna que ninguém se preocupou em remover. Então sinta-se bem aoexcluí-la.

Page 103: Data Science do zero: Primeiras regras com o Python

ParadoxodeSimpsonUmasurpresaincomumaoanalisardadoséoParadoxodeSimpson,emqueascorrelaçõespodemserenganosasquandoasvariáveisdeconfusãosãoignoradas.

Porexemplo, imaginequevocêpossa identificar todososseusmembroscomocientistasdedadosdaCostaLesteedaCostaOeste.Vocêdecideexaminarquaissãoosmaisamigáveis:

costa quantidadedemembros

médiadaquantidadedeamigos

CostaOeste

101 8.2

CostaLeste

103 6.5

CertamenteparecequeoscientistasdedadosdaCostaOestesãomaisamigáveisdoqueosdaCostaLeste.Seuscolegasdetrabalhoinvestememtodootipodeteorias no motivo pelo qual isso talvez aconteça: talvez seja o sol, o café, aproduçãoorgânicaouabrisadescontraídadoPacífico.

Ao brincar com os dados, você descobre algo muito estranho. Se você olharsomente para as pessoas com PhDs, os cientistas de dados da Costa Lestepossuem uma média maior de amigos. E, se você olhar para as pessoas semPhDs,oscientistasdedadosdaCostaLestetambémpossuemumamédiamaiordeamigos!

Page 104: Data Science do zero: Primeiras regras com o Python

Umavezquevocêverificaosdiplomasdosusuários,acorrelaçãovaiemdireçãooposta!AgrupandoosdadoscomoCostaLeste/OestemascarouofatodequeoscientistasdedadosdaCostaLestesedistorcemmaisintensamentecomostiposdePhDs.

Talfenômenosurgenomundorealcomalgumaregularidade.Opontochaveéque a correlação émedir a relação entre suas duas variáveiscom tudo omaissendoigual.Seassuasaulasdedadosfossematribuídasaleatoriamente,comosefossemclassificadascomoumexperimentobemprojetado,“pormaisquesejamiguais” pode não ser uma premissa terrível. Mas quando há um padrão maisprofundo na atribuição de classe, “por mais que sejam iguais” pode ser umapremissaterrível.

Oúnicomodorealdeevitarissoéconhecendoseusdadosefazendooquepuderpara ter certeza de que verificou pelos possíveis fatores de confusão.Evidentemente, nem sempre é possível. Se você não tivesse a informaçãoeducacional desses 200 cientistas de dados, você talvez concluísse que haviaalgoinerenteemaissociávelsobreaCostaOeste.

Page 105: Data Science do zero: Primeiras regras com o Python

AlgunsOutrosPontosdeAtençãosobreCorrelaçãoUma correlação de zero indica que não há uma relação linear entre as duasvariáveis.Porém,podemhaverváriostiposderelações.Porexemplo,se:

x=[-2,-1,0,1,2]y=[2,1,0,1,2]

entãoxeypossuemumacorrelaçãozero.Mas,certamente,têmumarelação—cadaelementodeyéigualaovalorabsolutodoelementocorrespondentedex.Oqueelesnãotêméumarelaçãoemquesabercomox_isecomparaamean(x)nosdáinformaçõessobrecomoy_isecomparaamean(y).Esseéotipoderelaçãoqueacorrelaçãoprocura.

Além do mais, a correlação não diz nada sobre o tamanho das relações. Asvariáveis:

x=[-2,1,0,1,2]y=[99.98,99.99,100,100.01,100.02]

estão perfeitamente correlacionadas, mas (dependendo do que você estámedindo)ébempossívelqueessarelaçãonãosejamuitointeressante.

Page 106: Data Science do zero: Primeiras regras com o Python

CorrelaçãoeCausalidadeVocêjádeveterescutadoalgumavezque“correlaçãonãoécausalidade”,maispossivelmente de uma pessoa pesquisando dados que impuseram desafios àspartesdavisãodemundoqueeleestavarelutanteemquestionar.Apesardisso,esteéumpontoimportante—sexeypossuemumafortecorrelação,issotalvezsignifiquequexcausay,queycausaxequecadaumcausaooutro,quealgumterceirofatorcausaambosoupodenãosignificarnada.

Considerearelaçãoentrenum_friendsedaily_minutes.ÉpossívelquetermaisamigosfaçacomqueosusuáriosdaDataSciencesterpassemmais temponosite.Essepode ser o caso se cada amigo postar uma certa quantidade de conteúdodiariamentepois,quantomaisamigosvocêtem,maistempovocêlevaparapôremdiasuasatualizações.

Porém, também é possível que, quantomais tempo você passe discutindo nosfóruns da DataSciencester, mais você encontrará e fará amizade com pessoasparecidascomvocê.Ouseja,passarmaistemponositefazcomqueosusuáriostenhammaisamigos.

Uma terceira possibilidade seria que os usuários mais dedicados com datasciencepassassemmais temponosite (porqueelesachammais interessante)eativamentecolecionassemmaisamigosdatascience(porqueelesnãoqueremseassociarcommaisninguém).

Uma maneira de se sentir mais confiante sobre causalidade é conduzirexperimentosaleatórios.Sevocêpodedividir seususuários aleatoriamente emdoisgruposcomdemografiaparecidaedaraumdosgruposumaexperiênciaumpouco diferente, logo você verá que experiências diferentes estão causandoresultadosdiferentes.

Porexemplo,sevocênãoseimportardeseracusadodefazerexperimentoscomseus usuários (http://nyti.ms/1L2DzEg), você pode escolher um subconjuntoaleatóriodeusuáriosemostraraelesoconteúdodesomenteumapartedosseusamigos.Seessesubconjuntosubsequentementepassarmenostemponosite,issolhedarámaisconfiançadequetermaisamigosfazpassarmaistemponosite.

Page 107: Data Science do zero: Primeiras regras com o Python

——

ParaMaisEsclarecimentosSciPy(http://bit.ly/1L2H0Lj),pandas(http://pandas.pydata.org),eStatsModels(http://bit.ly/1L2GQnc)vêmcomumagrandevariedadedefunçõesestatísticas.Estatísticaéimportante.(Ou,talvez,estatísticassãoimportantes?)Sevocêquiserserumbomcientistadedados,seriaumaboaideialerumlivrodidáticodeestatística.Muitosestãodisponíveisonline.Gostomuitodestesdois:

OpenIntroStatistics(http://bit.ly/1L2GKvG)OpenStaxIntroductoryStatistics(http://bit.ly/1L2GJrM)

Page 108: Data Science do zero: Primeiras regras com o Python

CAPÍTULO6

Probabilidade

Asleisdaprobabilidade,nogeraltãoverdadeiras,noparticulartãoenganosas.—EdwardGibbon

Édifícil praticardata science semalgumentendimentodeprobabilidadee suamatemática.IgualmenteànossaabordagemsobreestatísticanoCapítulo5,nosdedicaremosmuitoaeliminarmuitasdastecnicalidades.

Para os nossos propósitos, você deveria pensar em probabilidade como umaformadequantificaraincertezaassociadacomeventosescolhidosapartirdeumuniversodeles.Emvezdeestudartecnicamenteessesmétodos,penseemjogarum dado. O universo consiste de todos os resultados possíveis. Cadasubconjunto desses resultados é um evento; por exemplo, “o dado mostra onúmeroum”ou“odadomostraumnúmeroímpar”.

Destaforma,escrevemosP(E)parasignificar“aprobabilidadedoeventoE”.

Usaremos a teoria da probabilidade para construir modelos. Usaremos aprobabilidade para avaliar modelos. Usaremos a teoria da probabilidade emtodososlugares.

Alguémpoderia,setivessevontade,irbemafundonafilosofiadoqueateoriada probabilidade significa. (Melhor fazer isso com algumas cervejas.) Nãofaremosisso.

Page 109: Data Science do zero: Primeiras regras com o Python

DependênciaeIndependênciaAgrossomodo,dizemosquedoiseventosEeFsãodependentessesoubermosalgosobreseEocorrenosderinformaçõessobreseFocorre(evice-versa).Docontrário,sãoindependentes.

Por exemplo, se jogarmos uma moeda honesta duas vezes, sabendo que aprimeira jogadaé coroa,não temoscomosaber se a segunda jogadavaidaromesmo resultado. Esses eventos são independentes. Por outro lado, sesoubéssemos que a primeira jogada fosse coroa, certamente teríamos ainformação sobre se ambas as jogadas seriam cara. (Se a primeira jogada écoroa, então, definitivamente, não é o caso de que as duas jogadas são cara.)Essesdoiseventossãodependentes.

Matematicamente, dizemos que os dois eventos E e F são independentes se aprobabilidadedelesacontecereméoprodutodaprobabilidadedequecadaumdelesaconteça:

P(E,F)=P(E)P(F)

Noexemploanterior,aprobabilidadeda“primeirajogadasercoroa”éde1/2,eaprobabilidade de “ambas serem cara” é de 1/4, mas a probabilidade de “aprimeirajogadasercoroaeambasseremcara”é0.

Page 110: Data Science do zero: Primeiras regras com o Python

1.2.

ProbabilidadeCondicionalQuandoosdoiseventosEeFsãoindependentes,pordefinição,temos:

P(E,F)=P(E)P(F)

Senão sãonecessariamente independentes (e a probabilidadedeFnão for 0),logodefinimosaprobabilidadedeE“condicionadaaF”assim:

P(E|F)=P(E,F)/(P(F)

Vocêdeveria entender isso comoaprobabilidadedeEacontecerumavezquesabemosqueFacontece.

Geralmentereescrevemosdestaforma:

P(E,F)=P(E|F)P(F)

QuandoEeFsãoindependentes,vocêpodeverificarqueissoresultaem:

P(E|F)=P(E)

que é amaneiramatemática de explicar que saber que F ocorreu não nos dánenhumainformaçãoadicionalsobreseEocorreu.

Um exemplo comum e traiçoeiro envolve uma família com dois filhos(desconhecidos).

Sepresumirmosque:

ÉigualmentepossívelquecadacriançasejameninooumeninaOgênerodasegundacriançaéindependentedogênerodaprimeira,entãoo evento “nenhumamenina” tem a probabilidade de 1/4, o evento “umamenina, um menino” tem a probabilidade de 1/2 e o evento “duasmeninas”temaprobabilidadede1/4.

Agora,podemosperguntar:qualaprobabilidadedeoevento“asduascriançassão meninas” (B) ser condicionado pelo evento “a criança mais velha é umamenina”(G)?Usandoadefiniçãodeprobabilidadecondicional:

Page 111: Data Science do zero: Primeiras regras com o Python

P(B|G)=P(B,G)/P(G)=P(B)/P(G)=1/2

umavezqueoeventoBeG(“ambasascriançassãomeninaseacriançamaisvelhaéumamenina”)éapenasoeventoB.(Jásabendoqueasduascriançassãomeninas,éobrigatoriamenteverdadequeacriançamaisvelhasejamenina.)

Esseresultadotalvezcorrespondaasuaintuição.

Também poderíamos perguntar sobre a probabilidade do evento “as duascriançassãomeninas”sercondicionalaoevento“aomenosumadascriançasémenina”(L).Surpreendentemente,arespostaédiferentedeantes!

Anteriormente,oseventosBeL(“asduascriançassãomeninaseaomenosumadelaséumamenina”)éapenasoeventoB.Issosignificaquetemos:

P(B|L)=P(B,L)/P(L)=P(B)/P(L)=1/3

Comopodeseresseocaso?Bem,se tudoquevocêsabeéqueaomenosumadascriançasémenina,entãoéduasvezesmaisprovávelqueafamíliatenhaummeninoeumameninadoqueduasmeninas.

Podemosverificarissoao“gerar”váriasfamílias:defrandom_kid():

returnrandom.choice(["boy","girl"])

both_girls=0older_girl=0either_girl=0

random.seed(0)for_inrange(10000):

younger=random_kid()older=random_kid()ifolder=="girl":

older_girl+=1ifolder=="girl"andyounger=="girl":

both_girls+=1ifolder=="girl"oryounger=="girl":

either_girl+=1print"P(both|older):",both_girls/older_girl#0.514~1/2print"P(both|either):",both_girls/either_girl#0.342~1/3

Page 112: Data Science do zero: Primeiras regras com o Python

TeoremadeBayesUmdosmelhoresamigosdocientistadedadoséoTeoremadeBayes,oqualéuma maneira de “reverter” as probabilidades condicionais. Digamos queprecisamos saber a probabilidade de algum evento E ser condicionado àocorrência de outro evento F. Mas apenas temos a informação sobre aprobabilidadedaocorrênciadeFsendocondicionadoaE.Usandoadefiniçãodeprobabilidadecondicionalduasvezes,podemosdizerque:

P(E|F=P(E,F)/P(F)=P(F|E)P(E)/P(F)

OeventoFpodeserdivididoemdoiseventosmutuamenteexclusivos“FeE”e“FenãoE”.SeescrevermosEpara“nãoE”(porexemplo,“Enãoacontece”),logo:

entãotemos:

queécomooTeoremadeBayeséestabelecido.

Esse teorema é usado com frequência para demonstrar porqueos cientistas dedadossãomaisespertosdoquemédicos.imaginequeumadeterminadadoençaafete1acada10.000pessoas.Eimaginequehajaumtesteparaessadoençaquemostra o resultado correto (“doente” se você tem a doença e “não-doente” senão)99%dasvezes.

O que significa um teste positivo? Vamos usar T para o evento “seu teste épositivo”eDparaoevento“vocêtemadoença”.OTeoremadeBayesdizqueaprobabilidadedevocêteradoença,condicionalaotestepositivo,é:

AquivemosqueP(T|D),aprobabilidadedequealguémcomadoençaobtenhaumtestepositivo,é0,99.P(D),aprobabilidadedequequalquerpessoatenhaadoença é 1/10.000 = 0.0001. P (T|¬D), a probabilidade de que alguém sem adoença obtenha um teste positivo é 0,01. E P(¬D), a probabilidade de que

Page 113: Data Science do zero: Primeiras regras com o Python

qualquerpessoanãotenhaadoençaé0,9999.SevocêsubstituiressesnúmerosnoTeoremadeBayesvocêencontrará

P(D)|T=0.98%

Ou seja, menos de 1% das pessoas que obtém um teste positivo realmentepossuemadoença.

Isso presume que as pessoas fazem o teste de forma aleatória. Se apenas aspessoas que possuíssem alguns sintomas fizessem o teste, teríamos comocondiçãooevento“testepositivoesintomas”eonúmeroteriaapossibili-dadedeserbemmaior.

Enquantoesseéumcálculosimplesparaoscientistasdedados,amaioriadosmédicoschutariamqueP(D|T)seriapertode2.

Umaformamaisintuitivadeverissoéimaginarumapopulaçãodeummilhãodepessoas.Vocêesperariaque100delastivessemadoença,eque99dessas100obtivessemumtestepositivo.Poroutrolado,vocêesperariaque999.900delasnão tivessemadoença,eque9,999delasobtivessemumtestepositivo.Oquesignifica que você esperaria que somente 99 de (99 + 9999) testes positivosrealmentepossuíssemadoença.

Page 114: Data Science do zero: Primeiras regras com o Python

VariáveisAleatóriasUma variável aleatória é a variável cujos valores possíveis possuem umadistribuiçãode probabilidade associada.Umavariável aleatória bem simples éiguala1 seum lançamentodemoeda forcarae0 se forcoroa.Umamaneiramaiscomplicadaseriamedironúmerodecarasobservadasao lançaramoedadez vezes ou um valor escolhido de range(10), no qual todos os números têm amesmaprobabilidade.

Adistribuiçãoassociadadáasprobabilidadesqueavariávelpossuiemcadaumdeseusvalorespossíveis.Avariáveldolançamentodemoedaéiguala0comaprobabilidadede0,5e1comaprobabilidadede0,5.Avariávelrange(10)temumadistribuiçãoqueatribuiaprobabilidade0,1paracadaumdosnúmerosde0a9.

Às vezes falaremos sobre o valor esperado da variável aleatória, o qual é amédia de seus valores ponderados por suas probabilidades. A variável delançamentodamoedatemumvaloresperadode1/2(=0*1/2+1*1/2),eavariávelrange(10)temumvaloresperadode4,5.

As variáveis aleatórias podem ser condicionadas a eventos assim comooutroseventos.Voltandoaoexemplodasduascriançasda“ProbabilidadeCondicional”napágina70,seXforavariávelrandômicarepresentandoonúmerodemeninas,X é igual a 0 com probabilidade de¼, 1 com probabilidade de 1/2 e 2 comprobabilidadede¼.

Podemos definir uma nova variável Y que diz o número de meninascondicionadoa,pelomenos,umadascriançasserumamenina.Logo,Yéiguala1 comaprobabilidadede2/3 e 2 comprobabilidadede1/3.AvariávelZ é onúmerodemeninasqueécondicionadoaofilhomaisvelhosendoumameninaiguala1comprobabilidadede1/2e2comprobabilidadede1/2.

Namaioriadasvezes,usaremosasvariáveisaleatóriasimplícitasaoquefazemossemchamaraatençãoparaelas.Mas,sevocêolharmaisatentamente,vocêasverá.

Page 115: Data Science do zero: Primeiras regras com o Python

DistribuiçõesContínuasUmlançamentodemoedacorrespondeaumadistribuiçãodiscreta—umaqueassociaprobabilidadepositivacomresultadosdiscretos.Frequentemente,vamosquerermodelar as distribuições pormeio de um contínuo de resultados. (Paranossospropósitos,essesresultadossempreserãonúmerosreais,emboranãosejaocasonavidareal.)Porexemplo,adistribuiçãouniformecolocapesoigualemtodososnúmerosentre0e1.

Comoexistem infinitosnúmerosentre0e1, isso significaqueopesoqueeleatribui aos pontos individuais precisa ser exatamente 0. Por esse motivo,representamos uma distribuição contínua com uma função de densidade deprobabilidade (pdf, do inglês probability density function) tal que aprobabilidadedeverumvaloremumdeterminadointervaloéigualàintegraldafunçãodedensidadesobreointervalo.

Seseucálculointegralestiverenferrujado,amelhormaneiradeentenderissoéseadistribuiçãotemafunçãodedensidadef, logoaprobabilidadedeverumvalorentrexex+héaproximadamenteh*f(x)sehforpequeno.

Afunçãodedensidadeparaadistribuiçãouniformeé:defuniform_pdf(x):

return1ifx>=0andx<1else0

Aprobabilidadedeumvaloraleatórioseguidodedistribuiçãoestarentre0,2e0,3 é 1/10, como era de se esperar. random.random() de Python é uma variável(pseudo)aleatóriacomumadensidadeuniforme.

Estaremos frequentemente mais interessados na função de distribuiçãocumulativa (cdf, do inglês cumulative distribution function) que fornece aprobabilidade de umavariável aleatória sermenor ou igual a umdeterminadovalor. Não é difícil criar uma função de distribuição cumulativa para adistribuiçãouniforme(Figura6-1):

defuniform_cdf(x):"retornaaprobabilidadedeumavariávelaleatóriauniformeser<=x"ifx<0:return0#aaleatóriauniformenuncaémenordoque0

Page 116: Data Science do zero: Primeiras regras com o Python

elifx<1:returnx#porexemploP(X<=0.4)=0.4else:return1#aaleatóriauniformesempreémenordoque1

Figura6-1.Afunçãodedistribuiçãocumulativauniforme

Page 117: Data Science do zero: Primeiras regras com o Python

ADistribuiçãoNormalAdistribuiçãonormaléarainhadasdistribuições.Éumaclássicadistribuiçãodecurvaemformadesinoeédeterminadapordoisparâmetros:suamédiaμ(mi)eodesviopadrãoσ(sigma).Amédiaindicaondeosinoécentralizadoeodesviopadrãoindicaalarguradosino.

Elapossuiafunçãodedistribuição:

quepodemosimplementarcomo:defnormal_pdf(x,mu=0,sigma=1):

sqrt_two_pi=math.sqrt(2*math.pi)return(math.exp(-(x-mu)**2/2/sigma**2)/(sqrt_two_pi*sigma))

Na Figura 6-2, analisamos algumas dessas funções de densidade deprobabilidadeparavercomoelesficam:

xs=[x/10.0forxinrange(-50,50)]plt.plot(xs,[normal_pdf(x,sigma=1)forxinxs],'-',label='mu=0,sigma=1')plt.plot(xs,[normal_pdf(x,sigma=2)forxinxs],'--',label='mu=0,sigma=2')plt.plot(xs,[normal_pdf(x,sigma=0.5)forxinxs],':',label='mu=0,sigma=0.5')plt.plot(xs,[normal_pdf(x,mu=-1)forxinxs],'-.',label='mu=-1,sigma=1')plt.legend()plt.title("DiversasFunçõesdeDensidadedeProbabilidadeNormais")plt.show()

Page 118: Data Science do zero: Primeiras regras com o Python

Figura6-2.Diversasfunçõesdedensidadedeprobabilidadenormais

Échamadadedistribuiçãonormalpadrão quandoμ=0eσ=1.SeZéumavariávelaleatórianormalpadrão,então:

X=σZ+μ

tambéménormalmascomamédiaμeodesviopadrãoσ.Poroutrolado,seXéumavariávelaleatórianormalcommédiaμedesviopadrãoσ,

Z=(X-μ)/σ

éumavariávelnormalpadrão.

A função de distribuição cumulativa para a distribuição normal não pode serescrita de maneira “elementar”, mas podemos escrever usando math.erf

(http://en.wikipedia.org/wiki/Error_function)doPython:defnormal_cdf(x,mu=0,sigma=1):

return(1+math.erf((x-mu)/math.sqrt(2)/sigma))/2

Page 119: Data Science do zero: Primeiras regras com o Python

Novamente,naFigura6-3,vemosalguns:xs=[x/10.0forxinrange(-50,50)]plt.plot(xs,[normal_cdf(x,sigma=1)forxinxs],'-',label='mu=0,sigma=1')plt.plot(xs,[normal_cdf(x,sigma=2)forxinxs],'--',label='mu=0,sigma=2')plt.plot(xs,[normal_cdf(x,sigma=0.5)forxinxs],':',label='mu=0,sigma=0.5')plt.plot(xs,[normal_cdf(x,mu=-1)forxinxs],'-.',label='mu=-1,sigma=1')plt.legend(loc=4)#bottomrightplt.title("DiversasFunçõesdeDensidadedeDistribuiçãoCumulativa")plt.show()

Figura6-3.Diversasfunçõesdedistribuiçãocumulativa

Algumas vezes teremos que inverter normal_cdf para encontrar o valorcorrespondenteàprobabilidadeespecificada.Nãoexisteumaformasimplesdecomputar esse inverso, mas normal_cdf é contínuo e em crescimento, portantopodemos usar uma busca binária(http://en.wikipedia.org/wiki/Binary_search_algorithm):

definverse_normal_cdf(p,mu=0,sigma=1,tolerance=0.00001):

Page 120: Data Science do zero: Primeiras regras com o Python

"""encontraoinversomaispróximousandoabuscabinária"""

#senãoforpadrão,computaopadrãoeredimensionaifmu!=0orsigma!=1:

returnmu+sigma*inverse_normal_cdf(p,tolerance=tolerance)

low_z,low_p=-10.0,0#normal_cdf(-10)está(muitopertode)0hi_z,hi_p=10.0,1#normal_cdf(10)está(muitopertode)1whilehi_z-low_z>tolerance:

mid_z=(low_z+hi_z)/2#consideraopontodomeioeovalordamid_p=normal_cdf(mid_z)#funçãodedistribuiçãocumulativaláifmid_p<p:

#opontodomeioaindaestábaixo,procuraacimalow_z,low_p=mid_z,mid_p

elifmid_p>p:#opontodomeioaindaestáalto,procuraabaixohi_z,hi_p=mid_z,mid_p

else:break

returnmid_z

A função divide em dois intervalos repetidamente até diminuir para um Zpróximoosuficientedaprobabilidadedesejada.

Page 121: Data Science do zero: Primeiras regras com o Python

OTeoremadoLimiteCentralUmmotivoparaadistribuiçãonormalsertãoútiléoteoremadolimitecentral,quedizque(emessência)umavariávelaleatóriadefinidacomoamédiadeumagrande quantidade de variáveis aleatórias distribuídas independente eidenticamenteéelamesmaaproximadamentedistribuídanormalmente.

Emespecial,sex1,…,xnsãovariáveisaleatóriascommédiaμedesviopadrãoσ,esenforgrande,então:

estáaproximadamentedistribuídanormalmentecomamédiaμeodesviopadrão.Damesmaforma(masbemmaisútil),

estáaproximadamentedistribuídanormalmentecommédia0edesviopadrão1.

Uma maneira simples de ilustrar isso é considerando as variáveis aleatóriasbinomiais, as quais possuem dois parâmetros n e p. Uma variável aleatóriaBinomial(n,p) é apenas a soma de n variáveis aleatórias independentesBernoulli(p), e cada uma delas é igual a 1 com probabilidade p e 0 comprobabilidade1−p:

defbernoulli_trial(p):return1ifrandom.random()<pelse0

defbinomial(n,p):returnsum(bernoulli_trial(p)for_inrange(n))

Amédia de uma variável Bernoulli(p) é p, e seu desvio padrão Oteoremadolimitecentraldizque,conformenaumenta,avariávelBinomial(n,p)éaproximadamenteumavariávelaleatórianormalcomamédiaμ=npedesviopadrão Se analisarmos os dois, pode-se ver claramente asemelhança:

defmake_hist(p,n,num_points):

data=[binomial(n,p)for_inrange(num_points)]

Page 122: Data Science do zero: Primeiras regras com o Python

#usaumgráficodebarrasparaexibirasamostrarbinomiaisatuaishistogram=Counter(data)plt.bar([x-0.4forxinhistogram.keys()],

[v/num_pointsforvinhistogram.values()],0.8,color='0.75')

mu=p*nsigma=math.sqrt(n*p*(1-p))

#usaumgráficodelinhasparaexibirumaaproximaçãodanormalxs=range(min(data),max(data)+1)ys=[normal_cdf(i+0.5,mu,sigma)-normal_cdf(i-0.5,mu,sigma)

foriinxs]plt.plot(xs,ys)plt.title("DistribuiçãoBinomialvs.AproximaçãoNormal")plt.show()

Porexemplo,quandovocêchamamake_hist(0.75, 100, 10000),vocêobtémográficodaFigura6-4.

Figura6-4.Asaídademake_hist

Page 123: Data Science do zero: Primeiras regras com o Python

O principal dessa aproximação é que se você quiser saber a probabilidade de(digamos)umamoedahonesta cair 60 coroas em100 lançamentos, vocêpodeestimar a probabilidade de umaNormal(50,5) sermaior que 60, o que émaisfácil do que computar a função de distribuição cumulativaBinomial(100,0.5).(Emboranamaioriadasaplicaçõesépossívelusarumsoftwareestatísticoquecomputaquaisquerprobabilidadesquevocêquiser.)

Page 124: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosscipy.stats(http://bit.ly/1L2H0Lj)contémasfunçõesdedistribuiçãocumulativaededensidadedeprobabilidadeparaamaioriadasdistribuiçõesdeprobabilidadepopulares.Lembre-secomo,nofinaldoCapítulo5,eudissequeseriaumaboaideiaestudarcomumlivrodidáticodeestatística?Tambémseriaumaboaideiaestudarcomumlivrodidáticodeprobabilidade.OmelhorqueeuconheçoeestádisponívelonlineéoIntroductiontoProbability(http://bit.ly/1L2MTYI).

Page 125: Data Science do zero: Primeiras regras com o Python

CAPÍTULO7

HipóteseeInferência

Écaracterísticadeumapessoarealmenteinteligentesermovidapelaestatística.—GeorgeBernardShaw

Oque faremos com todas essas teorias de estatística e probabilidade?Aparteciênciadedatasciencefrequentementeenvolveformare testarhipóteses sobrenossosdadoseosprocessosqueosgeram.

Page 126: Data Science do zero: Primeiras regras com o Python

TesteEstatísticodeHipóteseCom frequência, como cientistas de dados, vamos querer testar se umadeterminadahipóteseéverdadeira.Paraosnossospropósitos, ashipóteses sãoafirmações como “esta é uma moeda honesta” ou “os cientistas de dadospreferemPythonaR”ou“seriamaisprovávelqueaspessoassaíssemdapáginasemleroconteúdosenósmostrássemosumanúnciopop-upcomumbotãodefecharpequenoedifícildeencontrar”quepossamsertraduzidasemestatísticassobre dados. Sob diversas premissas, tais estatísticas podem ser vistas comoobservações de variáveis aleatórias a partir de distribuições conhecidas, o quepermite que façamos declarações sobre as premissasmais prováveis de seremcorretas.

Em uma configuração clássica, temos a hipótese nulaH0 que representa umaposição padrão, e algumahipóteseH1 com a qual gostaríamos de compará-la.Usamos a estatística para decidir se rejeitamos H0 como falso ou não.Provavelmente,farámaissentidopormeiodeumexemplo.

Page 127: Data Science do zero: Primeiras regras com o Python

Exemplo:LançarUmaMoedaImagine que temos uma moeda e queremos testar para confirmar se ela éhonesta.Temosapremissadequeamoedapossuiaprobabilidadepdecaircara,entãonossahipótesenulaéqueamoedasejahonesta—ouseja,quep=0,5.Testaremosnovamentecontraahipótesealternativap≠0,5.

Em especial, nosso teste envolverá o lançamento da moeda em número n devezes e contando o número de caras X. Cada lançamento da moeda é umaTentativa de Bernoulli, o que significa que X é uma variável aleatóriaBinomial(n,p),que(comovimosnoCapítulo6)podemosaproximarusandoadistribuiçãonormal:

defnormal_approximation_to_binomial(n,p):"""encontramiesigmacorrespondendoaoBinomial(n,p)"""mu=p*nsigma=math.sqrt(p*(1-p)*n)returnmu,sigma

Semprequeumavariávelaleatóriasegueumadistribuiçãonormal,podemosusarnormal_ cdf para descobrir a probabilidade dos seus valores resultantes sereminternos(ouexternos)emumintervaloespecial:

#ocdfnormaléaprobabilidadequeavariávelestejaabaixodeumlimitenormal_probability_below=normal_cdf

#estáacimadolimitesenãoestiverabaixodefnormal_probability_above(lo,mu=0,sigma=1):

return1-normal_cdf(lo,mu,sigma)

#estáentreseformenosdoquehi,masnãomenordoquelodefnormal_probability_between(lo,hi,mu=0,sigma=1):

returnnormal_cdf(hi,mu,sigma)-normal_cdf(lo,mu,sigma)

#estáforasenãoestiverentredefnormal_probability_outside(lo,hi,mu=0,sigma=1):

return1-normal_probability_between(lo,hi,mu,sigma)

Tambémpodemosfazerocontrário—encontrararegiãosemabaouointervalo(simétrico)emtornodamédiaquecontribuiparaoníveldeprobabilidade.Porexemplo,sequisermosencontrarumintervalocentradonamédiacontendo60%

Page 128: Data Science do zero: Primeiras regras com o Python

de probabilidade, então encontraremos os cortes onde as abas inferiores esuperiorescontêm20%deprobabilidadecada(deixando60%):

defnormal_upper_bound(probability,mu=0,sigma=1):"""retornazparaquep(Z<=z)=probability"""returninverse_normal_cdf(probability,mu,sigma)

defnormal_lower_bound(probability,mu=0,sigma=1):"""retornazparaquep(Z>=z)=probability"""returninverse_normal_cdf(1-probability,mu,sigma)

defnormal_two_sided_bounds(probability,mu=0,sigma=1):"""retornaoslimitessimétricos(sobreamédia)quecontêmaprobabilidadeespecífica"""tail_probability=(1-probability)/2#limitesuperiordeveriatertail_probabilityacimaupper_bound=normal_lower_bound(tail_probability,mu,sigma)

#limiteinferiordeveriatertail_probabilityabaixolower_bound=normal_upper_bound(tail_probability,mu,sigma)

returnlower_bound,upper_bound

Em especial, digamos que escolhemos lançar umamoedan = 1000 vezes. Senossa hipótese de honestidade for verdadeira, X deveria ser distribuídonormalmentecommédia500edesviopadrãode15,8:

mu_0,sigma_0=normal_approximation_to_binomial(1000,0.5)

Precisamostomarumadecisãosobresignificância—dequantoéavontadequetemosdefazerumerrotipo1(“falsopositivo”),emquerejeitamosH0mesmoseforverdadeiro.Pormotivosperdidospelasmemóriasdahistória,essavontadeéconfiguradapara5%ou1%,geralmente.Vamosescolher5%.

ConsidereotestequerejeitaH0seXcairforadoslimitesdadospor:normal_two_sided_bounds(0.95,mu_0,sigma_0)#(469,531)

Presumindoquepsejaiguala0,5(porexemplo,H0éverdadeiro),háapenas5%de chance de observarmos que um X permanece fora desse intervalo, pois éexatamenteasignificânciaquequeríamos.Deoutraforma,seH0forverdadeiro,essetesteapresentaráoresultadocorretoaproximadamenteem19de20vezes.

Tambémestamos interessadosnopoder de um teste, que é a probabilidadedenãocometerumerrotipo2,noqualfalhamosemrejeitarH0mesmoelesendo

Page 129: Data Science do zero: Primeiras regras com o Python

falso.Afimdemediresseprocedimento,temosqueespecificaroquerealmentesignificaH0 ser falso. (Sabendo ao menos que p não é 0,5 não lhe dá umainformaçãosignificativasobreadistribuiçãodeX.)Emespecial,verificaremosoque acontece sep realmente for 0,55, a fim de que amoeda esteja levementeinclinadaasercara.

Nessecaso,podemoscalcularopoderdotestecom:#95%doslimitesbaseadosnapremissapé0,5lo,hi=normal_two_sided_bounds(0.95,mu_0,sigma_0)

#miesigmareaisbaseadosemp=0,55mu_1,sigma_1=normal_approximation_to_binomial(1000,0.55)

#umerrotipo2significaquefalhamosaorejeitarahipótesenula#queaconteceráquandoXaindaestiveremnossointervalooriginaltype_2_probability=normal_probability_between(lo,hi,mu_1,sigma_1)power=1-type_2_probability#0.887

Agora,imaginequeanossahipótesenulafossequeamoedanãoseriainclinadaacara,ouquep≤0,5.Nessecaso,queríamosumtesteunilateralquerejeitasseahipótese nula quando X fosse muito maior que 50 mas não quando X fossemenor. Portanto, um teste de significância de 5% envolveria usarnormal_probability_below para encontrar o corte abaixo dos 95% em que aprobabilidadeficaria:

hi=normal_upper_bound(0.95,mu_0,sigma_0)#é526(<531,jáqueprecisamosdemaisprobabilidadenaabasuperior)

type_2_probability=normal_probability_below(hi,mu_1,sigma_1)power=1-type_2_probability#0.936

Esse teste é mais poderoso, visto que ele não mais rejeitaH0 quandoX estáabaixode469(improváveldeacontecerseH1forverdadeiro)e,aoinvés,rejeitaH0quandoXestáentre526e531(prováveldeacontecerseH1forverdadeiro).

Page 130: Data Science do zero: Primeiras regras com o Python

p-valuesUmaoutramaneiradepensarsobreotesteanteriorenvolvep-values.Emvezdeescolherlimitescombaseemalgumaprobabilidadedecorte,nóscomputamosaprobabilidade—presumindo queH0 seja verdadeiro—que podemos ver umvaloraomenostãoextremoquantoaoquerealmenteobservamos.

Paraonossotestebilateralparaamoedahonesta,computamos:deftwo_sided_p_value(x,mu=0,sigma=1):

ifx>=mu:#sexformaiordoqueamédia,acoroaseráoqueformaiordoquexreturn2*normal_probability_above(x,mu,sigma)

else:#sexformenordoqueamédia,acoroaseráoqueformenordoquexreturn2*normal_probability_below(x,mu,sigma)

Sevíssemos530caras,computaríamos:two_sided_p_value(529.5,mu_0,sigma_0)#0.062

Porqueusamos529,5emvezde530?Issoéoquechamamosdecorreçãodecontinuidade (http://en.wikipedia.org/wiki/Continuity_correction). Reflete ofato de que normal_probability_between(529.5, 530.5, mu_0, sigma_0) é amelhor estimativa da probabilidade de ver 530 caras do quenormal_probability_between(530,531,mu_0,sigma_0)seria.

Desta forma, normal_probability_above(529.5, mu_0, sigma_0) é a melhorestimativadaprobabilidadedever aomenos530caras.Vocêdeve ternotadoquetambémusamosissonocódigoproduzidonaFigura6-4.

Umaformadeseconvencerdarelevânciadessaestimativaépormeiodeumasimulação:

extreme_value_count=0for_inrange(100000):

num_heads=sum(1ifrandom.random()<0.5else0#contagemdo#decarasfor_inrange(1000))#em1000lançamentos

ifnum_heads>=530ornum_heads<=470:#econtagemdafrequênciaextreme_value_count+=1#que#é'extrema'

printextreme_value_count/100000#0.062

Desdequep-value sejamaior do que a significância de 5%, não rejeitamos a

Page 131: Data Science do zero: Primeiras regras com o Python

hipótesenula.Sevíssemos532caras,op-valueseria:two_sided_p_value(531.5,mu_0,sigma_0)#0.0463

queémenordoqueasignificânciade5%,logo,rejeitaríamosahipótesenula.Éexatamenteomesmotestedeantes.Éapenasumaformadiferentedeabordaraestatística.

Damesmamaneira,teríamos:upper_p_value=normal_probability_abovelower_p_value=normal_probability_below

Senósvíssemos525carasparaotesteunilateral,computaríamos:upper_p_value(524.5,mu_0,sigma_0)#0.061

mostrandoquenãorejeitaríamosahipótesenula.Senósvíssemos527caras,acomputaçãoseria:

upper_p_value(526.5,mu_0,sigma_0)#0.047

enósrejeitaríamosahipótesenula.

Certifique-se de que seu dado está distribuído normalmente antes de usarnormal_probability_aboveparacomputarp-values.Asmemórias ruinsdedatascienceestãorepletasdeexemplosdepessoasopinandoqueachancedealgumeventoobservadoocorrer aleatoriamente éumaemummilhão, quandooqueeles realmente querem dizer é “a chance, presumindo que o dado sejadistribuídonormalmente”eébeminútilseodadonãoofor.

Existemdiversostestesestatísticosparaanormalidade,noentanto,atémesmoaelaboraçãodegráficodosdadoséumaboaideia.

Page 132: Data Science do zero: Primeiras regras com o Python

IntervalosdeConfiançaTemostestadohipótesessobreovalordaprobabilidadep,doresultadocaraqueéumparâmetrodadesconhecidadistribuição“cara”.Quandoocasoéesse,umaterceira abordagem é construir um intervalo de confiança em torno do valorobservadodoparâmetro.

Por exemplo, podemos estimar a probabilidade de uma moeda viciada aoanalisar o valor médio das variáveis Bernoulli correspondentes a cadalançamento— 1 se cara, 0 se coroa. Se nós observarmos 525 caras de 1000lançamentos,logoestimamospem0,525.

Quãoconfiantes podemos ser nessa estimativa?Bem, se soubéssemos o valorexato dep, o teorema de limite central (lembre-se de “O Teorema do LimiteCentral” na página 78) nos diz que a média daquelas variáveis Bernoullideveriamserquasenormais,comamédiapedesviopadrão:

math.sqrt(p*(1-p)/1000)

Aquinãoconhecemosp,portantousamosnossaestimativa:p_hat=525/1000mu=p_hatsigma=math.sqrt(p_hat*(1-p_hat)/1000)#0.0158

Isso não é inteiramente comprovado, mas as pessoas parecem fazê-lo dequalquer forma.Ao usar a aproximação normal, concluímos que somos “95%confiantes”dequeparâmetroseguintecontémoverdadeiroparâmetrop:

normal_two_sided_bounds(0.95,mu,sigma)#[0.4940,0.5560]

Essadeclaraçãoésobreointervaloenãosobrep.Vocêdeveriaentendercomouma premissa se fosse repetir o experimentomuitas vezes, 95% das vezes oparâmetro “verdadeiro” (que é o mesmo todas as vezes) ficaria dentro dointervalodeconfiançaobservada(quepodeserdiferenteacadavez).

Emespecialnãoconcluímosqueamoedasejaviciada,jáque0,5caidentrodenossointervalodeconfiança.

Se,emvezdisso,tivéssemosvisto540caras,teríamos:p_hat=540/1000

Page 133: Data Science do zero: Primeiras regras com o Python

mu=p_hatsigma=math.sqrt(p_hat*(1-p_hat)/1000)#0.0158normal_two_sided_bounds(0.95,mu,sigma)#[0.5091,0.5709]

Aqui, a “moeda honesta” não fica no intervalo de confiança. (A hipótese da“moeda honesta” não passaria no teste esperado 95% das vezes se fosseverdade.)

Page 134: Data Science do zero: Primeiras regras com o Python

P-HackingUm procedimento que rejeita a hipótese nula erroneamente somente 5% dasvezes vai— por definição— rejeitar erroneamente 5% das vezes a hipótesenula:

defrun_experiment():"""lançaumamoeda1000vezes,True=cara,False=coroa"""return[random.random()<0.5for_inrange(1000)]

defreject_fairness(experiment):"""usando5%dosníveisdesignificância"""num_heads=len([flipforflipinexperimentifflip])

returnnum_heads<469ornum_heads>531

random.seed(0)experiments=[run_experiment()for_inrange(1000)]num_rejections=len([experiment

forexperimentinexperimentsifreject_fairness(experiment)])

printnum_rejections#46

O que isso quer dizer é que você está tentando encontrar resultados“significativos”egeralmentevocêconsegue.Testehipótesessuficientescontraoseuconjuntodedadoseumdeles,certamente,parecerásignificante.Removaosvalores discrepantes certos, e será provável conseguir seu p-value abaixo de0,05. (Fizemos algo vagamente parecido em “Correlação” na página 62;percebeu?)

Istoé,porreter,chamadoP-hacking(http://bit.ly/1L2QtCr),eé,decertaforma,umaconsequênciada“inferênciaapartirdaestruturadosp-values”.Umartigoótimocriticandoessaabordagemé“TheEarthisRound”(http://bit.ly/1L2QJ4a).

Sevocêquerpraticarumaboaciência,vocêdeveriadeterminarsuashipótesesantesdeverificarosdados,deverialimparseusdadossempensarnashipóteses,edeveria ter emmentequep-valuesnão são substitutosparao senso comum.(Umaabordagemalternativaseria“InferênciaBayesiana”napágina88.)

Page 135: Data Science do zero: Primeiras regras com o Python

Exemplo:ExecutandoumTesteA/BUma de nossas responsabilidades iniciais na DataSciencester é testar aotimização, um eufemismo para tentar fazer com que as pessoas cliquem nosanúncios.Umde seus anunciantesdesenvolveuumabebida energéticavoltadaparaoscientistasdedados,eovice-presidentedePublicidadequerasuaajudapara escolher entre a propagandaA (“bomsabor!”) oupropagandaB (“menospolarização!”).

Por ser um cientista, você decide executar um experimento mostrando aosvisitantes do site uma das duas propagandas e registrando quantas pessoasclicamemcadaum.

Se990de1000visualizadoresdoanúncioAclicamnapropagandaenquantoque10 de 1000 visualizadores doB clicam, você pode ficar confiante de queA émelhordoqueB.Maseseasdiferençasnãosãotãograves?Aquiéondevocêusariainferênciaestatística.

DigamosquepessoasNAvejamoanúncioA,equenAcliquemnele.Podemospensar emcadavisualizaçãodo anúncio comoumaTentativadeBernoulli emque pA é a probabilidade de alguém clicar no anúncio A. Então (se NA forgrande,eéaqui)sabemosquenA/NAéaproximadamenteumavariávelaleatória

commédiapAedesviopadrãode

Igualmente,nB/NB é aproximadamente uma variável aleatória commédiapBe

desviopadrãodedefestimated_parameters(N,n):

p=n/Nsigma=math.sqrt(p*(1-p)/N)returnp,sigma

Sepresumirmosqueasduasnormaissãoindependentes(parecerazoável,jáqueaTentativadeBernoullideveriaser),entãosuasdiferençastambémdeveriamser

normaiscomamédiapB–pAeodesviopadrão

Équaseumatrapaça.Amatemáticasófuncionaexatamentedessejeitosevocê

Page 136: Data Science do zero: Primeiras regras com o Python

conheceosdesviospadrões.Aqui,estamosestimando-osapartirdosdados,oque significa que realmente deveríamos usar a distribuição t. Mas paraconjuntosdedadossuficientementegrandesnãofaztantadiferença.

IssosignificaquepodemostestarahipótesenulaquepAepBsãoamesma(ouseja,quepB–pAézero)usandoaestatística:

defa_b_test_statistic(N_A,n_A,N_B,n_B):p_A,sigma_A=estimated_parameters(N_A,n_A)p_B,sigma_B=estimated_parameters(N_B,n_B)return(p_B-p_A)/math.sqrt(sigma_A**2+sigma_B**2)

quedeveriaserumanormalpadrão.

Porexemplo,se“bomsabor”recebe200cliquesde1000visualizaçõese“menospolarização”recebe180cliquesde1000visualizações,aestatísticaéesta:

z=a_b_test_statistic(1000,200,1000,180)#-1.14

Aprobabilidadedevertaldiferençaseamédiafosserealmenteigualseria:two_sided_p_value(z)#0.254

queégrandeosuficienteevocênãoconseguesabersetaldiferençaexiste.Poroutrolado,se“menospolarização”recebessesomente150cliques,teríamos:

z=a_b_test_statistic(1000,200,1000,150)#-2.94two_sided_p_value(z)#0.003

que significa que há somente a probabilidade de 0,0003 que você veria taldiferençaseaspropagandasfossemigualmenteeficazes.

Page 137: Data Science do zero: Primeiras regras com o Python

InferênciaBayesianaOs procedimentos que vimos se dedicaram a fazer as declarações deprobabilidade sobre nossos testes: “há apenas uma chance de 3% de você terobservadotalestatísticaextremasenossahipótesenulafosseverdadeira”.

Uma abordagem alternativa para a inferência envolve tratar os parâmetrosdesconhecidoscomovariáveisaleatórias.Oanalista(ouseja,você)começacomuma distribuição anterior (a priori) para os parâmetros e usa os dadosobservadoseoTeoremadeBayesparareceberumaatualizaçãodadistribuiçãoposterior (a posteriori) para os parâmetros. Em vez de julgar a probabilidadesobreostestes,julgueaprobabilidadesobreosprópriosparâmetros.

Porexemplo,quandooparâmetrodesconhecidoéumaprobabilidade(comononossoexemplodelançamentodemoeda),frequentementeusamosumaanteriorapartirdadistribuiçãoBeta,colocandotodasasprobabilidadesentre0e1:

defB(alpha,beta):"""umaconstantenormalizadaparaqueaprobabilidadetotalseja1"""returnmath.gamma(alpha)*math.gamma(beta)/math.gamma(alpha+beta)

defbeta_pdf(x,alpha,beta):ifx<0orx>1:#sempesoforade[0,1]

return0returnx**(alpha-1)*(1-x)**(beta-1)/B(alpha,beta)

Emgeral,essadistribuiçãocentralizaseupesoem:alpha/(alpha+beta)

equantomaioresosalphaebetasão,mais“estreita”éadistribuição.

Porexemplo,sealphaebeta forem1,éapenasadistribuiçãouniforme(centradaem0,5,muitodispersa).Sealphaformuitomaiordoquebeta,amaioriadopesoficapertode1.E, se alpha formuitomenordoquebeta, amaioriadopeso ficapertode0.AFigura7-1mostraváriasdistribuiçõesBetasdiferentes.

Então, digamos que presumimos uma distribuição anterior em p. Talvez nãoqueremostomarumaposiçãoseamoedaforhonestaenósescolhermosalphaebetaparaambasserem

Page 138: Data Science do zero: Primeiras regras com o Python

1.Ou, talvez, tenhamos uma forte certeza de que dará cara 55% das vezes, eescolhemosalphaiguala55,betaiguala45.

Lançamosnossamoedamuitasvezesevemoshparaheads(cara)e tparatails(coroa).OTeoremadeBayes(eumpoucodematemáticaqueémuitoentediantepara eu tocar nesse assunto) nos diz que a distribuição posterior para p énovamenteumadistribuiçãobetamascomparâmetrosalpha+hebeta+t.

Nãoécoincidênciaqueadistribuiçãoposteriorsejanovamenteumadistribuiçãobeta. O número de caras é fornecido pela distribuição binomial, e a Beta éconjugada anterior (http://www.johndcook.com/blog/conjugate_prior_diagram/)dela.Issosignificaqueaqualquermomentoquevocêatualizaruma Beta anterior usando observações a partir do correspondente binomial,vocêreceberáumaBetaposterior.

Figura7-1.ExemplosdedistribuiçõesBeta

Digamosquevocêlanceamoeda10vezesmassóveja3caras.

Page 139: Data Science do zero: Primeiras regras com o Python

Se você tivesse começado com uma distribuição anterior uniforme (de certaforma se recusando a tomar uma posição sobre a honestidade damoeda), suadistribuição posterior seria uma Beta(4, 8), centrada próximo de 0,33. Já quevocê considerou todas as probabilidades igualmente possíveis, seu melhorpalpiteéalgobempertodaprobabilidadeobservada.

Sevocê tivessecomeçadocomumBeta(20,20) (acreditandoqueamoedaeramais ou menos honesta), sua distribuição posterior seria um Beta(23, 27)centrada próximo de 0,46, indicando uma segurança que talvez a moeda sejalevementeinclinadaparacoroa.

E se você começasse com um Beta(30, 10) (acreditando que a moeda estavainclinadaalançarcaraem75%),suadistribuiçãoposteriorseriadeumBeta(33,17),centradopróximode0,66.Nessecaso,vocêaindaacreditarianainclinaçãopara cara, mas menos do que acreditaria no início. Essas três distribuiçõesposterioresdiferentesestãoexibidasnaFigura7-2.

Figura7-2.Posterioressurgindodeanterioresdiferentes

Page 140: Data Science do zero: Primeiras regras com o Python

Se você lançasse uma moeda mais e mais vezes, a anterior teria menosimportância até eventualmente ter (quase) amesmadistribuiçãoposterior, semimportaremqualanteriorvocêcomeçou.

Porexemplo,nãoimportaainclinaçãoquevocêpensouqueamoedatinha,seriadifícil acreditarnissodepoisdever1000carasde2000 lançamentos (amenosquevocêsejaumlunáticoqueescolheumaanteriortipoBeta(1000000,1)).

O interessante é que isso permite que façamos declarações de probabilidadesobrehipóteses:“baseadonaanteriorenosdadosobservados,háapenas5%deprobabilidade que as caras da moeda estejam entre 49% e 51%”.Filosoficamente,émuitodiferentedeumadeclaraçãocomo“seamoedafossehonesta,esperaríamosobservardadostãoextremossomente5%dasvezes”.

O uso da inferência Bayesiana para testar hipóteses é considerado um poucocontroverso—emparteporquesuamatemáticapodesetornarcomplicadae,emparte,porcausadanaturezasubjetivadeseescolherumaanterior.Nãousaremosissoemmaisnenhumlugardestelivro,masébomsabersobreisso.

Page 141: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosQuasenemtocamosnasuperfíciedoquevocêdeveriasabersobreinferênciaestatística.OslivrosrecomendadosnofinaldoCapítulo5entramemmuitomaisdetalhes.ACourseraofereceocursoAnálisedeDadoseInferênciaEstatística(eminglês)queabordamuitosdessestópicos.

Page 142: Data Science do zero: Primeiras regras com o Python

CAPÍTULO8

GradienteDescendente

Aquelesquesegabamdeseusdescendentes,contamvantagemdoquedevemaosoutros.—Seneca

Frequentemente,aopraticardatascience, tentamosencontraromelhormodeloparaumadeterminadasituação.E,geralmente,a“melhor”significaráalgocomo“minimiza o erro do modelo” ou “maximiza a probabilidade do dado”. Emoutraspalavras,representaasoluçãoparaalgumtipodeproblemadeotimização.

Isso significa que precisaremos resolver uma quantidade de problemas deotimização.E,emespecial,precisaremosresolvê-losdozero.Nossaabordagemseráumatécnicachamadagradientedescendente,quesedispõemuitobemparaum tratamento do zero. Você talvez não ache muito animador, mas ela nospermitirá fazer coisas empolgantes no decorrer do livro, portanto, tenhapaciência.

Page 143: Data Science do zero: Primeiras regras com o Python

AIdeiaPorTrásdoGradienteDescendenteSuponhaquetenhamosafunção fque temcomoentradaumvetordenúmerosreaiseexibe,comosaída,umúniconúmeroreal.Talfunçãosimplesé:

defsum_of_squares(v):"""computaasomadoselementosaoquadradoemv"""returnsum(v_i**2forv_iinv)

Comfrequência,precisaremosmaximizar(ouminimizar)taisfunções.Ouseja,precisamosencontraraentradavqueproduzomaior(oumenor)valorpossível.

Parafunçõescomoanossa,ogradiente(sevocêselembradosseusestudosdecálculo,eleéovetordasderivadasparciais)mostraadireçãodaentradaemqueafunçãocrescemaisrapidamente.(Sevocênãoselembradosseusestudosdecálculo,acrediteemmimouprocurenainternet.)

Igualmente,umaabordagemparamaximizaruma funçãoépegarumpontodeinício aleatório, computar o gradiente, andar umpequeno passo na direção dogradiente (por exemplo, a direção que faz com que a função cresça mais), erepetir com o novo ponto de início. Da mesma forma, você pode tentarminimizarumafunçãoaoandarpoucospassosnadireçãooposta,comomostraaFigura8-1.

Page 144: Data Science do zero: Primeiras regras com o Python

Figura8-1.Encontrandoumamínimausandoumgradientedescendente

Se uma função possui uma mínima global única, é provável que esseprocedimentoaencontre.Seumafunçãopossuimínimasmúltiplas(locais),esseprocedimento talvez “encontre” a errada e, nesse caso, você talvez tenha queretomaroprocedimentoapartirdeváriospontosdeinício.Seumafunçãonãopossuimínima,entãoépossívelqueoprocedimentodureparasempre.

Page 145: Data Science do zero: Primeiras regras com o Python

EstimandooGradienteSeféumafunçãodeumavariável,suaderivadaemumpontoxindicacomof(x)muda quando fazemos uma mudança bem pequena em x. É definida como olimitedequocientesdiferenciais:

defdifference_quotient(f,x,h):return(f(x+h)-f(x))/h

conformehseaproximadezero.

(Muitos alunos de cálculo em potencial foram atrapalhados pela definiçãomatemática de limite. Aqui vamos trapacear e simplesmente dizer que elasignificaoquevocêachaquesignifica.)

Figura8-2.Aproximandoumaderivadacomumquocientediferencial

Page 146: Data Science do zero: Primeiras regras com o Python

A derivada é a inclinação da linha tangente em (x, f(x)), enquanto o quocientediferencial é a inclinação da linha-não-tão-tangente que passa por (x+h, f(x+h)).Conformeh vai ficandomenor, a linha-não-tão-tangente chega cada vezmaispertodalinhatangente(Figura8-2).

Paramuitasfunçõeséfácilcalcularasderivadascomexatidão.Porexemplo,afunçãosquare:

defsquare(x):returnx*x

temaderivada:defderivative(x):

return2*x

que você pode verificar— se você estiver com vontade— ao explicitamentecomputaroquocientediferencialetomandoolimite.

E se você não pudesse (ou não quisesse) encontrar o gradiente? Embora nãopossamos ter limites com Python, podemos estimar derivadas ao avaliar oquocientediferencialporumpequenoe.AFigura8-3mostraosresultadosparatalestimativa:

derivative_estimate=partial(difference_quotient,square,h=0.00001)

#planejamostrarquesãobasicamenteomesmoimportmatplotlib.pyplotaspltx=range(-10,10)plt.title("ActualDerivativesvs.Estimates")plt.plot(x,map(derivative,x),'rx',label='Actual')#vermelhoxplt.plot(x,map(derivative_estimate,x),'b+',label='Estimate')#azul+plt.legend(loc=9)plt.show()

Page 147: Data Science do zero: Primeiras regras com o Python

Figura8-3.Abondadedaaproximaçãodoquocientediferencial

Quandoféumafunçãodemuitasvariáveis,possuimúltiplasderivadasparciais,cada uma indicando como f muda quando fazemos pequenas mudanças emapenasumadasvariáveisdeentrada.

Calculamossuaderivadaparciali-ésimoaotratá-lacomoumafunçãodeapenasai-ésimavariável,contendoasoutrasvariáveisfixas:

defpartial_difference_quotient(f,v,i,h):"""computaoi-ésimoquocientediferencialparcialdefemv"""w=[v_j+(hifj==ielse0)#adicionahaoelementoi-ésimodev

forj,v_jinenumerate(v)]

return(f(w)-f(v))/h

depoisdoquepodemosestimarogradientedomesmojeito:defestimate_gradient(f,v,h=0.00001):

return[partial_difference_quotient(f,v,i,h)fori,_inenumerate(v)]

Page 148: Data Science do zero: Primeiras regras com o Python

A maior desvantagem da abordagem “estimar usando os quocientesdiferenciais” é sair caro em termos de computação. Se v tem o tamanho n,estimate_gradient tem que avaliar f em 2n entradas diferentes. Se você estáestimandogradientesumapósooutro,estáfazendomuitomaistrabalhoextra.

Page 149: Data Science do zero: Primeiras regras com o Python

UsandooGradienteÉfácilverqueafunçãosum_of_squarestemseumínimovalorquandosuaentradavé um vetor de zeros. Mas imagine que não sabíamos disso. Usaremos osgradientes para encontrar o mínimo entre todos os vetores tridimensionais.Pegaremosumponto inicialaleatórioeandaremospequenospassosnadireçãoopostadogradiente,atéchegarmosemumpontoemqueogradientesejamuitopequeno:

defstep(v,direction,step_size):"""movestep_sizenadireçãoapartirdev"""return[v_i+step_size*direction_i

forv_i,direction_iinzip(v,direction)]

defsum_of_squares_gradient(v):return[2*v_iforv_iinv]

#escolheumpontoinicialaleatóriov=[random.randint(-10,10)foriinrange(3)]

tolerance=0.0000001

whileTrue:gradient=sum_of_squares_gradient(v)#computaogradienteemvnext_v=step(v,gradient,-0.01)#pegaumpassogradientenegativoifdistance(next_v,v)<tolerance:#paraseestivermosconvergindo

breakv=next_v#continuasenãoestivermos

Sevocêcodificarisso,saberáqueelesempreterminacomumvmuitopróximoa[0,0,0].Quantomenorforatolerance,maispróximoeleserá.

Page 150: Data Science do zero: Primeiras regras com o Python

•••

EscolhendooTamanhodoPróximoPassoEmbora a lógicade semover emdireçãoaogradiente esteja clara, adistâncianãoestá.Defato,escolherotamanhodopróximopassoémaisumaartedoqueumaciência.Asopçõesmaispopularessão:

UsarumpassodetamanhofixoDiminuirgradualmenteotamanhodopassoacadavezAcadapasso,escolherotamanhodopassoqueminimizeovalordafunçãoobjetiva

A última opção parece perfeita mas é, na prática, uma computação custosa.Podemosaproximá-laaotentarumavariedadedetamanhosdepassoseescolherumqueresultenomenorvalordafunçãoobjetiva:

step_sizes=[100,10,1,0.1,0.01,0.001,0.0001,0.00001]

É possível que determinados tamanhos de passos resultarão em entradasinválidasparanossafunção.Então,vocêprecisarácriarumafunção“aplicaçãosegura” que retorna infinito (que nunca deveria ser o mínimo de nada) paraentradasinválidas:

defsafe(f):"""retornaumanovafunçãoqueéigualaf,excetoqueeleexibeinfinitocomosaídatodavezquefproduzumerro"""defsafe_f(*args,**kwargs):

try:returnf(*args,**kwargs)

except:returnfloat('inf')#issosignifica“infinito”emPython

returnsafe_f

Page 151: Data Science do zero: Primeiras regras com o Python

JuntandoTudoNogeral,temosalgumatarget_fnquequeremosminimizar,etambémtemososeugradient_fn. Por exemplo, target_fn poderia representar erros emummodelo comouma função dos seus parâmetros, e talvez queiramos encontrar os parâmetrosqueproduzemosmenoreserrospossíveis.

Alémdomais,digamosqueescolhemos(dealgumaforma)umvalorinicialparaos parâmetros theta_0. Logo, podemos implementar o gradiente descendentecomo:

defminimize_batch(target_fn,gradient_fn,theta_0,tolerance=0.000001):"""usaogradientedescendenteparaencontrarthetaqueminimizeafunçãoalvo"""

step_sizes=[100,10,1,0.1,0.01,0.001,0.0001,0.00001]

theta=theta_0#ajustathetaparaovalorinicialtarget_fn=safe(target_fn)#versãoseguradetarget_fnvalue=target_fn(theta)#valorqueestamosminimizando

whileTrue:gradient=gradient_fn(theta)next_thetas=[step(theta,gradient,-step_size)

forstep_sizeinstep_sizes]#escolheaquelequeminimizaafunçãodeerronext_theta=min(next_thetas,key=target_fn)next_value=target_fn(next_theta)

#paraseestivermos“convergindo”ifabs(value-next_value)<tolerance:returnthetaelse:theta,value=next_theta,next_value

Chamamosdeminimize_batchporque,paracadapassodogradiente,eleconsideraoconjuntointeirodedados(devidoaotarget_fnretornaroerronoconjuntodedadosinteiro).Napróxima seção,veremosumaabordagemalternativaqueconsideraapenasumpontodecadavez.

Àsvezesvamosquerermaximizarumafunçãoepodemosfazê-laaominimizarseunegativo(quepossuiumgradientenegativocorrespondente):

defnegate(f):"""retornaumafunçãoque,paraqualquerentrada,xretorna-f(x)"""returnlambda*args,**kwargs:-f(*args,**kwargs)

Page 152: Data Science do zero: Primeiras regras com o Python

defnegate_all(f):"""omesmoquandofretornaumalistadenúmeros"""returnlambda*args,**kwargs:[-yforyinf(*args,**kwargs)]

defmaximize_batch(target_fn,gradient_fn,theta_0,tolerance=0.000001):returnminimize_batch(negate(target_fn),

negate_all(gradient_fn),theta_0,tolerance)

Page 153: Data Science do zero: Primeiras regras com o Python

GradienteDescendenteEstocásticoConforme mencionamos anteriormente, usaremos com frequência o gradientedescendenteparaescolherosparâmetrosdeummodelodemodoqueminimizealguma noção de erro. Ao usar o grupo de abordagens anteriores, cada passogradienterequerquenósfaçamosumaprevisãoecomputemosogradienteparaoconjuntodedadosinteiro,fazendocomquecadapassolevemaistempo.

Normalmente, essas funções de erro são aditivas, o que significa que o erroprevistonoconjuntodedadosinteiroésimplesmenteasomadoserrospreditivosparacadaponto.

Quando o caso é esse, podemos aplicar uma técnica chamada gradientedescendenteestocástico,quecomputaogradiente(eandaumpasso)apenasumpontodecadavez.Elecirculasobrenossosdadosrepetidamenteatéalcançarumpontodeparada.

Durantecadaciclo,vamosquereriterarsobrenossosdadosemordemaleatória:defin_random_order(data):

"""geradorretornaoselementosdodadoemordemaleatória"""indexes=[ifori,_inenumerate(data)]#criaumalistadeíndicesrandom.shuffle(indexes)#osembaralhaforiinindexes:#retornaosdadosnaquelaordemyielddata[i]

Andaremosumpassogradienteparacadapontodedados.Essemétododeixaapossibilidadedecircularmospróximosaummínimoparasempre,entãoquandopararmos de obter melhorias, diminuiremos o tamanho do passo e,eventualmente,pararemos:

defminimize_stochastic(target_fn,gradient_fn,x,y,theta_0,alpha_0=0.01):

data=zip(x,y)theta=theta_0#palpiteinicialalpha=alpha_0#tamanhodopassoinicialmin_theta,min_value=None,float("inf")#omínimoatéagoraiterations_with_no_improvement=0

#seformosaté100iteraçõessemmelhorias,paramoswhileiterations_with_no_improvement<100:

value=sum(target_fn(x_i,y_i,theta)forx_i,y_iindata)

ifvalue<min_value:

Page 154: Data Science do zero: Primeiras regras com o Python

#seachouumnovomínimo,lembre-se#evolteparaotamanhodopassooriginalmin_theta,min_value=theta,valueiterations_with_no_improvement=0alpha=alpha_0

else:#docontrário,nãoestamosmelhorando,portantotente#diminuirotamanhodopassoiterations_with_no_improvement+=1alpha*=0.9

#eandeumpassogradienteparatodosospontosdedadosforx_i,y_iinin_random_order(data):

gradient_i=gradient_fn(x_i,y_i,theta)theta=vector_subtract(theta,scalar_multiply(alpha,gradient_i))

returnmin_theta

A versão estocástica será tipicamente mais rápida do que a versão batch.Naturalmente,vamosquererumaversãoquemaximizedamesmaforma:

defmaximize_stochastic(target_fn,gradient_fn,x,y,theta_0,alpha_0=0.01):returnminimize_stochastic(negate(target_fn),

negate_all(gradient_fn),x,y,theta_0,alpha_0)

Page 155: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosContinuelendo!Usaremosgradientedescendentepararesolverproblemaspelorestantedolivro.Nesteponto,vocêjáestácansadodemeverrecomendaraleituradelivrosdidáticos.Seservirdeconsolo,ActiveCalculus(http://gvsu.edu/s/xr/)parecemaislegaldoqueoslivrosdidáticosdecálculocomqueeuaprendi.scikit-learnpossuiummódulodeGradienteDescendenteEstocástico(http://scikit-learn.org/stable/modules/sgd.html).Nãoétãogeralquantoonossoemalgunspontosemaisgeralemoutros.Apesardeque,namaioriadassituaçõesdomundoreal,vocêusarábibliotecasnasquaisaotimizaçãojáestarápronta,entãovocênãoteráquesepreocuparcomelas(exatoquandonãofuncionarcorretamente,oque,umdia,inevitavelmente,acontecerá).

Page 156: Data Science do zero: Primeiras regras com o Python

CAPÍTULO9

ObtendoDados

Paraescrevê-lo,levoutrêsmeses;paraconcebê-lo,trêsminutos;paracoletarosdadosnele,todaaminhavida.

—F.ScottFitzgerald

Parasetornarumcientistadedados,vocêprecisadedados.Naverdade,comoumcientistadedados,vocêpassaráumaembaraçosagrandefraçãodoseutempoadquirindo,limpandoetransformandodados.Emumaemergência,vocêsemprepode inserirosdadosvocêmesmo (ou, sevocê tiverminions, façaos fazê-lo)mas, geralmente, não é um bom uso do seu tempo. Neste capítulo,consideraremosmaneirasdiferentesdeobterdadosparaPythonenosformatosadequados.

Page 157: Data Science do zero: Primeiras regras com o Python

stdinestdoutSe você executar seus scripts de Python na linha de comando, você podecanalizar(pipe)osdadospormeiodelesusandosys.stdinesys.stdout.Porexemplo,esteéumscriptquelê linhasde textoedevolveasquecombinaremcomumaexpressãoregular:

#egrep.pyimportsys,re

#sys.argvéalistadosargumentosdalinhadecomandos#sys.argv[0]éonomedoprogramaemsi#sys.argv[1]seráoregexespecificadonalinhadecomandosregex=sys.argv[1]

#paracadalinhapassadapeloscriptforlineinsys.stdin:

#secombinarcomoregex,escreva-oparaostdoutifre.search(regex,line):

sys.stdout.write(line)

Eesteéumquecontaaslinhasrecebidaseexibeacontagem:#line_count.pyimportsys

count=0forlineinsys.stdin:

count+=1

#printvaiparasys.stdoutprintcount

Vocêpoderiausá-losparacontarquantaslinhasdeumarquivocontêmnúmeros.NoWindows,vocêusaria:

typeSomeFile.txt|pythonegrep.py"[0-9]"|pythonline_count.py

enquantoquenosistemaUnix:catSomeFile.txt|pythonegrep.py"[0-9]"|pythonline_count.py

Este é o caractere pipe |, que significa “use a saída do comando da esquerdacomo a entrada do comando da direita”. Você pode construir encadeamentos(pipelines)elaboradosdeprocessamentodedadosdessaforma.

Page 158: Data Science do zero: Primeiras regras com o Python

SevocêestáusandooWindows,podedeixarapartePythonforadestecomando:

typeSomeFile.txt|egrep.py"[0-9]"|line_count.py

SevocêestánosistemaUnix,talcomandotalvezrequeiraumpoucomaisdetrabalhoparaserfeito(http://bit.ly/1L2Wgb7).

Igualmente,estescriptcontaaspalavrasemsuaentradaeexibeasmaiscomuns:#most_common_words.pyimportsysfromcollectionsimportCounter

#passaonúmerodepalavrascomoprimeiroargumentotry:

num_words=int(sys.argv[1])except:

print"usage:most_common_words.pynum_words"sys.exit(1)#códigodesaídanão-zeroindicaerro

counter=Counter(word.lower()#palavrasemminúsculasforlineinsys.stdin#forwordinline.strip().split()#seseparamporespaçosifword)#pulaas'palavras'vazias

forword,countincounter.most_common(num_words):sys.stdout.write(str(count))sys.stdout.write("\t")sys.stdout.write(word)sys.stdout.write("\n")

depoisdisso,vocêpoderiafazeralgocomo:C:\DataScience>typethe_bible.txt|pythonmost_common_words.py1064193the51380and34753of13643to12799that12560in10263he9840shall8987unto8836for

Se você for um programador familiarizado com Unix, provavelmente você estáfamiliarizadocomumagrandevariedadedeferramentasdelinhasdecomando(porexemplo, egrep) que são construídos dentro do seu sistema operacional eprovavelmentevocêsaspreferedoqueconstruirasuaprópriadozero.Aindaassim,

Page 159: Data Science do zero: Primeiras regras com o Python

ébomsaberquevocêpodeseprecisar.

Page 160: Data Science do zero: Primeiras regras com o Python

LendoArquivosVocê tambémpode ler a partir de e escrever nos arquivos diretamente no seucódigo.Pythonfacilitaotrabalhocomarquivos.

OBásicodeArquivosTextoO primeiro passo para trabalhar com arquivos de texto é obter um objeto dearquivousandoopen:

#'r'significasomenteleiturafile_for_reading=open('reading_file.txt','r')

#'w'éescrever--destruiráoarquivoseelejáexistir!file_for_writing=open('writing_file.txt','w')

#'a'éanexar--paraadicionaraofinaldoarquivofile_for_appending=open('appending_file.txt','a')

#nãoseesqueçadefecharosarquivosaoterminarfile_for_writing.close()

Comoémuitofácilesquecerdefecharosarquivos,vocêdeveriasempreusá-losem um bloco with, pois no término de cada um eles serão fechadosautomaticamente:

withopen(filename,'r')asf:data=function_that_gets_data_from(f)

#nestepontofjáfoifechado,nãotenteusá-loprocess(data)

Sevocêprecisarlerumarquivodetextointeiro,vocêpodeapenasiterarsobreaslinhasdoarquivousandofor:

starts_with_hash=0

withopen('input.txt','r')asf:forlineinfile:#observecadalinhadoarquivo

ifre.match("^#",line):#useumregexparaversecomeçacom'#'starts_with_hash+=1#secomeçar,adicione1àcontagem

Todalinhaquevocêobtémdessemodoterminaemumcaracteredenovalinha(newline), logo,vocêvaiquerer strip() removê-lo frequentementeantesde fazer

Page 161: Data Science do zero: Primeiras regras com o Python

qualquercoisa.

Porexemplo,imaginequevocêtenhaumarquivocheiodeendereçosdee-mail,umpor linha e que você precisa gerar umhistograma de domínios.As regrasparaextrairosdomínioscorretamentesãosutis(porexemplo,aListaPúblicadeSufixo emhttps://publicsuffix.org), uma boa primeira aproximação é pegar aspartes dos endereços de e-mails que vêm depois do @. (O que nos dá umarelaçãoerradaparaendereç[email protected].)

defget_domain(email_address):"""separano'@'eretornanaúltimaparte"""returnemail_address.lower().split("@")[-1]

withopen('email_addresses.txt','r')asf:domain_counts=Counter(get_domain(line.strip())

forlineinfif"@"inline)

ArquivosdelimitadosOendereçode e-mail hipotético que acabamosde processar temumendereçoporlinha.Commaisfrequência,vocêtrabalharácomarquivoscommuitosdadosemcada linha.Taisarquivossãoseparadosporvírgula (comma-separated)oupor tabulação (tab-separated). Cada linha possui diversos campos, com umavírgula(ouumatabulação)indicandoondeumcampoterminaeooutrocomeça.

Começaaficarcomplicadoquandovocêtemcamposcomvírgulas,tabulaçõesenewlinesneles(inevitavelmentevocêterá).Poressemotivo,équasesempreumerro tentar analisar sozinho.Emvez disso, você deveria usar omodulo csv doPython (ou as bibliotecas pandas). Por razões técnicas— fique à vontade paraculpar aMicrosoft— você deve sempre trabalhar com arquivos csv nomodobinário incluindo um b depois de r ou w (veja Stack Overflow emhttp://bit.ly/1L2Y7wl).

Seseuarquivonãopossuircabeçalho (oquesignificaquevocêquerquecadalinhasejacomouma list quedeposita emvocêo fardode saberoconteúdodecadacoluna),vocêpodeusarcsv.readerparaiterarsobreaslinhas,cadaqualseráumalistaapropriadamenteseparada.

Porexemplo, se tivéssemosumarquivodelimitadopor tabulaçãodepreçosde

Page 162: Data Science do zero: Primeiras regras com o Python

ações:6/20/2014AAPL90.916/20/2014MSFT41.686/20/2014FB64.56/19/2014AAPL91.866/19/2014MSFT41.516/19/2014FB64.34

poderíamosprocessá-loscom:importcsv

withopen('tab_delimited_stock_prices.txt','rb')asf:reader=csv.reader(f,delimiter='\t')forrowinreader:

date=row[0]symbol=row[1]closing_price=float(row[2])process(date,symbol,closing_price)

Seseuarquivopossuicabeçalhos:date:symbol:closing_price6/20/2014:AAPL:90.916/20/2014:MSFT:41.686/20/2014:FB:64.5

vocêpodepularalinha(comumachamadainicialparareader.next())ouobtercadalinhacomoumdict(comoscabeçalhoscomochaves)usandocsv.DictReader:

withopen('colon_delimited_stock_prices.txt','rb')asf:reader=csv.DictReader(f,delimiter=':')forrowinreader:

date=row["date"]symbol=row["symbol"]closing_price=float(row["closing_price"])process(date,symbol,closing_price)

Mesmo se seu arquivo não tiver cabeçalhos você ainda pode usarDictReader aopassarachaveaelescomooparâmetrofieldnames.

Damesmaforma,vocêpodetranscreverosdadosdelimitadosusandocsv.writer:today_prices={'AAPL':90.91,'MSFT':41.68,'FB':64.5}

withopen('comma_delimited_stock_prices.txt','wb')asf:writer=csv.writer(f,delimiter=',')forstock,priceintoday_prices.items():

writer.writerow([stock,price])

Page 163: Data Science do zero: Primeiras regras com o Python

csv.writerfaráacoisacertaseseuscampospossuíremvírgulas.Seuescritorfeitoàmãoprovavelmentenão.Porexemplo,sevocêtentar:

results=[["test1","success","Monday"],["test2","success,kindof","Tuesday"],["test3","failure,kindof","Wednesday"],["test4","failure,utter","Thursday"]]

#nãofaçaisso!withopen('bad_csv.txt','wb')asf:

forrowinresults:f.write(",".join(map(str,row)))#talveztenhamuitasvírgulasnele!f.write("\n")#alinhapodeternewlinestambém!

Vocêacabarácomumarquivocsvqueseparececom:test1,success,Mondaytest2,success,kindof,Tuesdaytest3,failure,kindof,Wednesdaytest4,failure,utter,Thursday

eninguémmaisconseguiráentender.

Page 164: Data Science do zero: Primeiras regras com o Python

ExtraindoDadosdaInternetOutramaneiradeseobterdadoséextraindo-osdaswebpages.Pesquisarpáginasdawebémuitofácil;extrairinformaçõesestruturadasesignificativasnãoétãofácil.

HTMLeSuaSubsequentePesquisaAs páginas na internet são escritas emHTML, na qual o texto (idealmente) émarcadoemelementoseatributos:

<html><head><title>Awebpage</title></head><body><pid="author">JoelGrus</p><pid="subject">DataScience</p></body></html>

Em um mundo perfeito, em que todas as páginas da Internet são marcadassemanticamenteanossofavor,seríamoscapazesdeextrairdadosusandoregrascomo“encontreoelemento<p>cujoidésubjecteretorneotextoqueelecontém”.Nomundo real,HTMLnãoémuitobemformulado,muitomenoscomentado.Issosignificaqueprecisaremosdeajudaparaentendertudoisso.

Para extrair dados do HTML, usaremos a biblioteca BeautifulSoup(http://www.crummy.com/software/BeautifulSoup/),poiselaconstróiumaárvoreapartirdevárioselementosdeumapáginaeforneceumasimplesinterfaceparaacessá-los. Enquanto eu escrevo este livro, a versão mais recente éBeautifulSoup4.3.2(pipinstallbeautifulsoup4),queéaqueusaremos.Tambémusaremos as bibliotecas de pedidos (pip install requests), que é umamaneiramaissimpáticadefazerpedidos(http://docs.python-requests.org/en/latest/)aoHTTPdoqueparaqualquercoisaconstruídadentrodePython.

OinterpretadorHTMLembutidoemPythonnãoé tãoflexível,oquesignificaquenemsempreelesedábemcomoHTMLquenãopossuiboaformação.Poressemotivo,usaremosuminterpretadordiferente,queprecisamosinstalar:

Page 165: Data Science do zero: Primeiras regras com o Python

pipinstallhtml5lib

Para usar o Beautiful Soup, teremos que passar um pouco de HTML para afunçãoBeautifulSoup().Emnossosexemplos,esteseráoresultadodeumachamadapararequests.get:

frombs4importBeautifulSoupimportrequestshtml=requests.get("http://www.example.com").textsoup=BeautifulSoup(html,'html5lib')

depoisdisso,iremoslongeusandopoucosmétodossimples.

Trabalharemos,namaiorparte,comobjetosTagquecorrespondemàsmarcações(tags)representandoaestruturadeumapáginaHTML.

Porexemplo,paraencontraraprimeiramarcação<p>vocêpodeusar:first_paragraph=soup.find('p')#ousomentesoup.p

VocêpodeobteroconteúdodotextodeumaTagusandoapropriedadetext:first_paragraph_text=soup.p.textfirst_paragraph_words=soup.p.text.split()

Evocêpodeextrairosatributosdeumamarcaçãotratando-acomoumdict:first_paragraph_id=soup.p['id']#surgeKeyErrorsenãotiver'id'first_paragraph_id2=soup.p.get('id')#retornaNonesenãotiver'id'

Vocêpodeobtermúltiplasmarcaçõesaomesmotempo:all_paragraphs=soup.find_all('p')#ouapenassoup('p')paragraphs_with_ids=[pforpinsoup('p')ifp.get('id')]

Frequentemente,vocêencontrarámarcaçõescomumaclassespecífica:important_paragraphs=soup('p',{'class':'important'})important_paragraphs2=soup('p','important')important_paragraphs3=[pforpinsoup('p')

if'important'inp.get('class',[])]

Você pode combiná-los para implementar uma lógica mais elaborada. Porexemplo,sevocêquiserencontrartodoelemento<span>queestácontidodentrodeumelemento<div>,vocêpoderiafazerassim:

#atenção,vairetornaromesmospanmúltiplasvezes#seeleficardentrodemúltiplosdivs#sejamaisespertoseesseforocaso

Page 166: Data Science do zero: Primeiras regras com o Python

spans_inside_divs=[spanfordivinsoup('div')#paracada<div>napáginaforspanindiv('span')]#encontracada<span>dentro

Essepunhadoderecursospermitiráquefaçamosbastantecoisa.Sevocêprecisarfazercoisasmaiscomplicadas(ouseécurioso),verifiqueadocumentação.

Naturalmente,qualquerdadoquesejaimportantepodenãoserclassificadocomoclass="important". Você terá que inspecionar com cuidado a fonte do HTML,raciocinarcomsua lógicaseletiva,e sepreocuparcomcasosextremospara secertificarqueseudadoestácorreto.Veremosumexemplo.

Exemplo:LivrosO’ReillySobreDadosUminvestidorempotencialparaaDataSciencesterachaquedadossãoapenasumamoda passageira. Para provar que ele está errado, você decide examinarquantos livros sobre dados aO'Reilly publicou ao longo dos anos. Depois defuçarpelosite,vocêencontramuitaspáginasdelivrossobredados(evídeos)e30itensexploráveisemumapáginadediretórioscomURLscomoesta:

http://shop.oreilly.com/category/browse-subjects/data.do?sortby=publicationDate&page=1

Amenosquevocêsejaumidiota(eamenosquevocêqueiraqueseuextratorseja banido), sempre que você quiser extrair dados de umwebsite, você deveverificarprimeiroseelepossuialgumtipodepolíticadeacesso.Olhandoem:

http://oreilly.com/terms/

nãoparecehavernadaproibindoesseprojeto.Afimdesermosbonscidadãos,tambémdevemos procurar pelo arquivo robots.txt que dizem aoswebcrawlers(programas que coletam dados da internet) como se comportar. As partesimportantesemhttp://shop.oreilly.com/robots.txtsão:

Crawl-delay:30Request-rate:1/30

Aprimeira linhanosdizquedevemosesperar30 segundosentreumpedidoeoutro; a segunda, que devemos pedir uma página a cada 30 segundos. Então,basicamente,elassãoduasformasdiferentesdedizeramesmacoisa.(Existemoutras linhas que indicam diretórios para não serem extraídos, mas elas nãoincluemnossaURL,portantoestamosbem.)

Page 167: Data Science do zero: Primeiras regras com o Python

Há sempre a possibilidade que a O'Reilly vá remodelar seu site e quebrar toda alógicadestaseção.Fareioqueeupuderparaprevenirisso,claro,maseunãopossuomuita influência sobreeles.Embora, se cadaumdevocêspudesseconvencer cadaumquevocêsconhecemacomprarumexemplardestelivro…

Para descobrir como extrair os dados, vamos baixar uma daquelas páginas ejogá-lasnoBeautifulSoup:

#vocênãoprecisadividiraurldestaformaamenosqueelaprecisecaberemumlivrourl="http://shop.oreilly.com/category/browse-subjects/"+\

"data.do?sortby=publicationDate&page=1"soup=BeautifulSoup(requests.get(url).text,'html5lib')

Sevocêvira fontedapágina (noseunavegador, cliquecomobotãodireitoeselecione(“Exibircódigo-fonte”ou“Exibircódigo-fontedapágina”ouqualqueroutraopçãoquesepareçacomisso),vocêveráquecadalivro(ouvídeo)pareceestar contido unicamente em um elemento <td> da célula da tabela cuja class éthumbtext.Estaé(emversãoresumida)oHTMLrelevanteparaumlivro:

<tdclass="thumbtext"><divclass="thumbcontainer">

<divclass="thumbdiv"><ahref="/product/9781118903407.do"><imgsrc="..."/></a></div>

</div><divclass="widthchange"><divclass="thumbheader">

<ahref="/product/9781118903407.do">GettingaBigDataJobForDummies</a></div><divclass="AuthorName">ByJasonWilliamson</div><spanclass="directorydate">December2014</span><divstyle="clear:both;">

<divid="146350"><spanclass="pricelabel">

Ebook:<spanclass="price">$29.99</span>

</span></div></div></div></td>

Umbomprimeiropassoéencontrartodososelementosdemarcaçãotdthumbtext:

Page 168: Data Science do zero: Primeiras regras com o Python

tds=soup('td','thumbtext')printlen(tds)#30

Gostaríamosdefiltrarosvídeos.(Esseaspiranteainvestidorsóseimpressionacomlivros.)SeinspecionarmosoHTMLumpoucomaisalém,vemosquecadatdcontémumoumaiselementosspancujaclassépricelabel,ecujotextoseparececomEbook: ouVideo: ouPrint:. Parece que os vídeos contêm apenas um pricelabel,cujo texto começa com Video (após remover os espaços importantes). Dessemodo,podemostestarparavídeoscom:

defis_video(td):"""éumvídeosetiverexatamenteumpricelabel,eseotextocorridodentrodopricelabelcomeçarcom'Video"""pricelabels=td('span','pricelabel')return(len(pricelabels)==1and

pricelabels[0].text.strip().startswith("Video"))

printlen([tdfortdintdsifnotis_video(td)])#21paramim,talvezsejadiferenteparavocê

Agoraestamosprontosparacomeçarapuxarosdadosparaforadoselementostd.Parecequeo títulodo livroéo textodentrodamarcação<a>dentrode<divclass="thumbheader">:

title=td.find("div","thumbheader").a.text

OsautoresestãonotextodeAuthorName<div>.ElessãointroduzidosporumBy(doqual queremos nos livrar) e separados por vírgulas (que queremos separar,depoisteremosquenoslivrardosespaços):

author_name=td.find('div','AuthorName').textauthors=[x.strip()forxinre.sub("^By","",author_name).split(",")]

OISBNpareceestarcontidonolinkqueestáemthumbheader<div>:isbn_link=td.find("div","thumbheader").a.get("href")

#re.matchcapturaapartedoregexemparêntesesisbn=re.match("/product/(.*)\.do",isbn_link).group(1)

Eadataésóoconteúdode<spanclass="directorydate">:date=td.find("span","directorydate").text.strip()

Vamoscolocartudojuntoemumafunção:

Page 169: Data Science do zero: Primeiras regras com o Python

defbook_info(td):"""dadoumamarcaçãoBeautifulSoup<td>representandoumlivro,extraiosdetalhesdolivroeretornaumdict"""

title=td.find("div","thumbheader").a.textby_author=td.find('div','AuthorName').textauthors=[x.strip()forxinre.sub("^By","",by_author).split(",")]isbn_link=td.find("div","thumbheader").a.get("href")isbn=re.match("/product/(.*)\.do",isbn_link).groups()[0]date=td.find("span","directorydate").text.strip()

return{"title":title,"authors":authors,"isbn":isbn,"date":date

}

Agoraestamosprontosparaextrair:frombs4importBeautifulSoupimportrequestsfromtimeimportsleepbase_url="http://shop.oreilly.com/category/browse-subjects/"+\

"data.do?sortby=publicationDate&page="

books=[]

NUM_PAGES=31#naépocadaescritadestelivro,provavelmentemaisagora

forpage_numinrange(1,NUM_PAGES+1):print"soupingpage",page_num,",",len(books),"foundsofar"url=base_url+str(page_num)soup=BeautifulSoup(requests.get(url).text,'html5lib')

fortdinsoup('td','thumbtext'):ifnotis_video(td):books.append(book_info(td))

#agorasejaumbomcidadãoerespeiteosrobots.txt!sleep(30)

ExtrairdadosdeHTMLdestaformaémaisumaartedoqueumaciênciadosdados.Existemoutras incontáveis lógicasencontre-os-livroseencontre-o-títuloque teriamfuncionadobemtambém.

Agoraquecoletamososdados,podemostraçaronúmerodelivrospublicadosacadaano(Figura9-1):

defget_year(book):

Page 170: Data Science do zero: Primeiras regras com o Python

"""book["date"]separececom'November2014'entãoprecisamosdividirnoespaçoeentãopegarosegundopedaço"""returnint(book["date"].split()[1])

#2014éoúltimoanocompletodedados(quandoeufizisso)year_counts=Counter(get_year(book)forbookinbooks

ifget_year(book)<=2014)

importmatplotlib.pyplotaspltyears=sorted(year_counts)book_counts=[year_counts[year]foryearinyears]plt.plot(years,book_counts)plt.ylabel("#delivrosdedados")plt.title("AÁreadeDadosÉGrande!")plt.show()

Figura9-1.Númerodelivrosdedadosporano

Infelizmente,oaspiranteainvestidorolhaparaográficoedecideque2013foia“taxamáximadedados”.

Page 171: Data Science do zero: Primeiras regras com o Python

UsandoAPIsMuitos websites e serviços web fornecem interfaces de programação deaplicativos (APIs), permitindo que você solicite os dados em formatoestruturado.Issopoupaotrabalhodeterqueextraí-los!

JSON(eXML)ComooHTTPéumprotocoloparaatransferênciadetexto,osdadosquevocêsolicitapormeiodeumaAPIdawebdeveserserializadaemformatodestring.Geralmente, essa serialização usa o JavaScript Object Notation (JSON). OsobjetosJavaScriptseparecembastantecomosdictsdoPython,oquefacilitaainterpretaçãodesuasstrings:

{"title":"DataScienceBook","author":"JoelGrus","publicationYear":2014,"topics":["data","science","datascience"]}

PodemosanalisarJSONusandoomódulojsondoPython.Emespecial,usaremosafunçãoloads,quedesserializaumastringrepresentandoumobjetoJSONemumobjetoPython:

importjsonserialized="""{"title":"DataScienceBook",

"author":"JoelGrus","publicationYear":2014,"topics":["data","science","datascience"]}"""

#analisaojsonparacriarumdictdoPythondeserialized=json.loads(serialized)if"datascience"indeserialized["topics"]:

printdeserialized

Àsvezes,umprovedorAPIteodeiaeforneceapenasrespostasemXML:<Book><Title>DataScienceBook</Title>

<Author>JoelGrus</Author><PublicationYear>2014</PublicationYear><Topics><Topic>data</Topic><Topic>science</Topic>

Page 172: Data Science do zero: Primeiras regras com o Python

<Topic>datascience</Topic></Topics>

</Book>

VocêpodeusarBeautifulSoupparaobterosdadosdoXMLdamesmaformacomousamosparaobterdoHTML;verifiqueasuadocumentaçãoparadetalhes.

UsandoUmaAPINãoAutenticadaAmaioriadasAPIsdehojeemdiarequerquevocêprimeiroasautentiqueafimdepoderusá-las.Enquantonãonos resignamosaessapolítica,elacriamuitospadrõesextrasquedistorcemanossaexposição.Assim,daremosumaprimeiraolhada na API do GitHub (http://developer.github.com/v3/), com a qual vocêpodepraticarcoisassimplesnão-autenticadas:

importrequests,jsonendpoint="https://api.github.com/users/joelgrus/repos"

repos=json.loads(requests.get(endpoint).text)

Nessemomento, reposéuma listdedictsdoPython,cadaumarepresentandoumrepositóriopúbliconaminha contaGitHub. (Sinta-se àvontadepara substituirseunomedeusuárioepegarseurepositóriodedadosGitHub.VocêpossuiumcontaGithub,certo?)

Podemosusarissoparadescobriremquaismesesediasdasemanatenhomaistendências para criar um repositório. O único problema é que as datas narespostasãostrings(Unicode):

u'created_at':u'2013-07-05T02:02:28Z'

OPythonnãovemcomumbomanalisadordedatas,entãoteremosqueinstalarum:

pipinstallpython-dateutil

doqualvocêprovavelmentesóprecisarádafunçãodateutil.parser.parse:fromdateutil.parserimportparse

dates=[parse(repo["created_at"])forrepoinrepos]month_counts=Counter(date.monthfordateindates)weekday_counts=Counter(date.weekday()fordateindates)

Da mesma forma, você pode obter as linguagens dos meus cinco últimosrepositórios:

Page 173: Data Science do zero: Primeiras regras com o Python

last_5_repositories=sorted(repos,key=lambdar:r["created_at"],reverse=True)[:5]

last_5_languages=[repo["language"]forrepoinlast_5_repositories]

Basicamente,nãotrabalharemoscomAPIsnessenívelbaixode“façaospedidoseanaliseasrespostasvocêmesmo”.UmdosbenefíciosdeseusarPythonéquealguém já construiu uma biblioteca para quase qualquer API que você estejainteressado em acessar. Quando elas são bem-feitas, elas podem te poupar demuitosproblemasaotentarentenderosdetalhesmaiscabeludosdoacessoAPI.(Quandoelasnão sãobem-feitas,ouquandosãobaseadasemversõesextintasdasAPIscorrespondentes,elaspodemdarenormesdoresdecabeça.)

Apesar disso, você talvez tenha que implantar seu próprio acesso à bibliotecaAPI (ou, mais provável, depurar porque a de alguém não está funcionando),portanto,ébomsaberdealgunsdetalhes.

EncontrandoAPIsSevocêprecisadedadosdeumsiteespecífico,procurepordesenvolvedoresouaseçãodeAPIdositeparadetalhes,etenteprocurarnawebpor“python__api”para encontrar uma biblioteca. Existe umaAPIRottenTomatoes para Python.Existemmúltiplascamadas(wrappers)paraaAPIKlout,paraaAPIYelp,paraaAPIIMDB,eassimpordiante.

SevocêestáprocurandoporlistasdeAPIsquetenhamascamadasPython,doisdiretórios estão em Python API (http://www.pythonapi.com) e Python forBeginners(http://bit.ly/1L35VOR).

SevocêquerumdiretóriodeAPIswebmaisabrangente(semnecessariamenteas camadas Python), um bom recurso é o Programmable Web(http://www.programmableweb.com),oqualpossuiumenormediretóriodeAPIscategorizados.

Esedepoisdetudoissovocênãoencontraroqueprecisa,hásempreaopçãodeextração,oúltimorefúgiodeumcientistadedados.

Page 174: Data Science do zero: Primeiras regras com o Python

1.2.

3.4.

5.6.7.8.

Exemplo:UsandoasAPIsdoTwitterOTwitteréumfontefantásticadedadoscomoqualtrabalhar.Vocêpodeusá-loparaverasnotíciasemtemporeal.Vocêpodeusá-loparamedirasreaçõesaoseventos atuais. Você pode usá-lo para encontrar links relacionados a tópicosespecíficos.Vocêpodeusá-loparapraticamente tudoquevocêpossa imaginar,contantoquevocêconsigaacessoaosseusdados.Evocêpode teresseacessopormeiodasAPIs.

Para interagir com as APIs do Twitter, usaremos a biblioteca Twython emhttps://github.com/ryanmcgrath/twython, (pip install twython). Existem algumasbibliotecasPythonparaoTwitterporaí,mas foicomessaqueeuobtivemaissucesso.Sinta-seencorajadoaexplorarasoutrastambém!

ObtendoCredenciaisPara usar as APIs do Twitter, você deve ter algumas credenciais (logo, vocêprecisadeumacontanoTwitter,edeveriaterdequalquerformaparafazerpartedavivaeamigávelcomunidade#datasciencenoTwitter).Comotodasasinstruçõesque se relacionam com os websites que eu não controlo, talvez isto se torneobsoletoemalgummomento,masesperoquefuncioneporumtempo.(Apesarde eles já teremmudado pelomenos uma vez enquanto eu estava escrevendoestelivro,entãoboasorte!)

Váparahttps://apps.twitter.com/.Sevocênãoestiverlogado,cliqueemEntrareinsiraseunomedeusuárioesenhadoTwitter.CliqueemCriarumNovoAplicativo.Dê a ele um nome (como “Data Science”) e uma descrição, e coloquequalquerURLcomowebsite(nãoimportaqual).DeixeaURLderetornoembranco.ConcordecomosTermosdeServiçoecliqueemCriar.Anoteachaveeosegredodoconsumidor.Cliqueem“Criarmeutokendeacesso”.Anoteotokendeacessoeodesegredo(talvezvocêtenhaqueatualizara

Page 175: Data Science do zero: Primeiras regras com o Python

página).

A chave e o segredo do consumidor dizem ao Twitter qual aplicação estáacessando suasAPIs, enquanto que o token de acesso e o token de acesso dosegredodizemaoTwitterquemestáacessandosuasAPIs.Sevocê jáusousuaconta do Twitter para entrar em outro site, a página “clique para autorizar”estavagerandoumtokendeacessoparaaquelesiteusara fimdeconvenceroTwitter que foi você (ou, ao menos, agindo como se fosse você). Como nãoprecisamosdafuncionalidade“deixarqualquerumentrar”,podemossobrevivercomotokendeacessoeotokendeacessodosegredogerados.

Achave/segredodoconsumidore a chave/segredodo tokendeacessodevemser tratadas como senhas. Você não deve compartilhá-las, publicá-las em seulivro ou acessá-las no repositório público Github. Uma solução simples éarmazená-las em um arquivo credentials.json que não é acessado e usar seucódigojson.loadspararecuperá-la.

UsandoTwython

Primeiro observaremos o Search API(https://dev.twitter.com/docs/api/1.1/get/search/tweets), o qual requer apenas achaveeosegredodoconsumidorenãootokendeacessoousegredo:

fromtwythonimportTwython

twitter=Twython(CONSUMER_KEY,CONSUMER_SECRET)

#searchfortweetscontainingthephrase"datascience"forstatusintwitter.search(q='"datascience"')["statuses"]:

user=status["user"]["screen_name"].encode('utf-8')text=status["text"].encode('utf-8')printuser,":",textprint

O.encode("utf-8") é necessário para lidar como fato de queos tweets geralmentecontêm caracteresUnicode com os quais print não pode lidar. (Se você deixar delado,vocêdevereceberumUnicodeEncodeError.)

Équase certo que emalgummomento de sua carreira de data science você vai sedeparar com sérios problemas Unicode e, em algum momento, você precisará sereferiràdocumentaçãoPython(http://bit.ly/1ycODJw)ourelutantementecomeçarausarPython3,oquallidamuitomelhorcomtextoUnicode.

Sevocêexecutarisso,vocêdeverárecebertweetscomo:

Page 176: Data Science do zero: Primeiras regras com o Python

haithemnyc:Datascientistswiththetechnicalsavvy&analyticalchopstoderivemeaningfrombigdataareindemand.http://t.co/HsF9Q0dShP

RPubsRecent:DataSciencehttp://t.co/6hcHUz2PHM

spleonard1:Using#dplyrin#Rtoworkthroughaprocrastinatedassignmentfor@[email protected].

Isso não émuito interessante, principalmente porque oSearchAPI doTwitterapenasmostraapartedosresultadosqueelequer.Quandovocêestápraticandodata science,vocêvaiquerercadavezmais tweets.ÉaíqueoStreamingAPI(http://bit.ly/1ycOEgG)éútil.Elepermitequevocêseconecteà (umaamostrade) avalanche Twitter. Para usá-lo, você precisará se identificar usando seustokensdeacesso.

AfimdeacessaroStreamingAPIcomTwython,precisamosdefinirumaclasseque herde TwythonStreamer e que anule o método on_success (e possivelmente seumétodoon_error):

fromtwythonimportTwythonStreamer

#anexardadosàvariávelglobalébempobre#massimplificaoexemplotweets=[]

classMyStreamer(TwythonStreamer):"""nossaprópriasubclassedeTwythonStreamerqueespecificacomointeragircomostream"""

defon_success(self,data):"""oquefazemosquandootwitternosenviadados?aquiosdadosserãoumdictdePythonrepresentandoumtweet"""

#quercoletarapenastweetsdalínguainglesaifdata['lang']=='en':tweets.append(data)print"receivedtweet#",len(tweets)

#paraquandocoletaosuficienteiflen(tweets)>=1000:self.disconnect()

defon_error(self,status_code,data):printstatus_code,dataself.disconnect()

MyStreamer vai se conectar o stream do Twitter e esperar que o Twitter oabasteça de dados. Toda vez que ele receber dados (aqui, um tweet érepresentadoporumobjetodePython),elepassaparaométodoon_success,queo

Page 177: Data Science do zero: Primeiras regras com o Python

anexa à nossa lista de tweets se sua língua for inglesa, e então desconecta ostreamerapóstercoletado1000tweets.

Tudooquerestaparainicializá-loefazê-lofuncionar:stream=MyStreamer(CONSUMER_KEY,CONSUMER_SECRET,

ACCESS_TOKEN,ACCESS_TOKEN_SECRET)

#começaaconsumirstatuspúblicosquecontenhamapalavra-chave'data'stream.statuses.filter(track='data')#sequiséssemoscomeçaraconsumirumaamostrade*all*statuspúblicos#stream.statuses.sample()

Eleexecutaráatécoletar1000tweets(ouatéencontrarumerro)eiráparareénessemomentoquevocêpode começar a inicializar tais tweets. Por exemplo,vocêpoderiaencontrarashashtagsmaisfamosascom:

top_hashtags=Counter(hashtag['text'].lower()fortweetintweetsforhashtagintweet["entities"]["hashtags"])

printtop_hashtags.most_common(5)

Cada tweet contém muitos dados. Você pode passear ou mergulhar fundo nadocumentaçãoAPIdoTwitter(https://dev.twitter.com/overview/api/tweets).

Emumprojetoquenãosejadebrincadeira,vocênãogostariadedependerdeumalist in-memorypara armazenar seus tweets.Emvezdisso,vocêos salvaria emumarquivooubancodedados,paraquevocêpossatê-lospermanentemente.

Page 178: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentospandas(http://pandas.pydata.org/)éabibliotecaprimáriacomaqualostiposdedatascienceusamparatrabalhar(e,emespecial,importar)dados.Scrapy(http://scrapy.org/)éumabibliotecacheiaderecursosparaconstruirextratoresdawebmaiscomplicados,quefazemcoisascomoseguirlinksdesconhecidos.

Page 179: Data Science do zero: Primeiras regras com o Python

CAPÍTULO10

TrabalhandocomDados

Osespecialistas,muitasvezes,possuemmaisdadosdoquejuízo.—ColinPowell

Trabalhar comdados é umaarte e umaciência.Temosdiscutidomais sobre apartecientífica,masnestecapítuloconsideraremosumpoucodearte.

Page 180: Data Science do zero: Primeiras regras com o Python

ExplorandoSeusDadosApósidentificarasquestõesquevocêtemtentadorespondereterpostoasmãosemalgunsdados,vocêpodeficar tentadoa irmais fundo,começaraconstruirmodelos e obter respostas. Mas você deveria resistir a esse impulso. Seuprimeiropassodeveriaserexplorarseusdados.

ExplorandoDadosUnidimensionaisOcasomaissimpleséquandovocêtemumconjuntodedadosunidimensional,apenasumacoleçãodenúmeros.Porexemplo,elespoderiamseramediadiáriade minutos que cada usuário passa no seu site, o número de vezes que cadacoleçãodevídeostutoriaisdedatasciencefoivistaouonúmerodepáginasdecadalivrodedatasciencenasuabiblioteca.

Um primeiro passo inevitável é computar algumas estatísticas sumárias. Vocêgostariadesaberquantospontosdedadosvocêtem,omenor,omaior,amédiaeodesviopadrão.

Mas nem isso tudo fornece, necessariamente, um bom entendimento. Umpróximo passo adequado é criar um histograma para agrupar seus dados emagrupamentos(buckets)discretosecontarquantospontosvãoparacadaum:

defbucketize(point,bucket_size):"""reduzaopontoparaopróximomúltiplomaisbaixodebucket_size"""returnbucket_size*math.floor(point/bucket_size)

defmake_histogram(points,bucket_size):"""agrupaospontosecontaquantosemcadabucket"""returnCounter(bucketize(point,bucket_size)forpointinpoints)

defplot_histogram(points,bucket_size,title=""):histogram=make_histogram(points,bucket_size)plt.bar(histogram.keys(),histogram.values(),width=bucket_size)plt.title(title)plt.show()

Porexemplo,considereosseguintesconjuntosdedados:random.seed(0)

#uniformeentre–100e100uniform=[200*random.random()-100for_inrange(10000)]

Page 181: Data Science do zero: Primeiras regras com o Python

#distribuiçãonormalcommédia0,desviopadrão57normal=[57*inverse_normal_cdf(random.random())

for_inrange(10000)]

Ambos possuem médias próximas a 0 e desvios padrões próximos a 58. Noentanto, possuem distribuições bem diferentes. A Figura 10-1 mostra adistribuiçãodeuniform:

plot_histogram(uniform,10,"HistogramadeUniform")

jáaFigura10-2mostraadistribuiçãodenormal:plot_histogram(normal,10,"HistogramaNormal")

Nesse caso, as duas distribuições possuem max e min muito diferentes, masmesmosabendoissonãoseriasuficienteparaentendercomoelasdiferem.

Figura10-1.Histogramadeuniform

Page 182: Data Science do zero: Primeiras regras com o Python

DuasDimensõesAgora imagine que você tenha um conjunto de dados com duas dimensões.Talvez, além deminutos diários, você também tenha anos de experiência emdata science. Certamente, você gostaria de entender cada dimensãoindividualmente.Masvocêtambémdevequererdispersarosdados.

Porexemplo,considereoutroconjuntodedadosfalso:defrandom_normal():

"""retornaumdesenhoaleatóriodeumadistribuiçãonormalpadrão"""returninverse_normal_cdf(random.random())

xs=[random_normal()for_inrange(1000)]ys1=[x+random_normal()/2forxinxs]ys2=[-x+random_normal()/2forxinxs]

Sevocêfosseexecutarplot_histogramemys1eys2,você teriagráficosmuitoparecidos (aliás, ambos são distribuídos normalmente com a mesma média edesviopadrão).

Page 183: Data Science do zero: Primeiras regras com o Python

Figura10-2.Histogramadenormal

Mas cada um teria distribuições conjuntas diferentes com xs, como mostra aFigura10-3:

plt.scatter(xs,ys1,marker='.',color='black',label='ys1')plt.scatter(xs,ys2,marker='.',color='gray',label='ys2')plt.xlabel('xs')plt.ylabel('ys')plt.legend(loc=9)plt.title("DistribuiçõesConjuntasMuitoDiferentes")plt.show()

Figura10-3.Dispersãodedoisysdiferentes

Essadiferençatambémseriaaparentesevocêobservasseascorrelações:printcorrelation(xs,ys1)#0.9printcorrelation(xs,ys2)#-0.9

Page 184: Data Science do zero: Primeiras regras com o Python

MuitasDimensõesCom muitas dimensões, você gostaria de saber como todas as dimensões serelacionam umas com as outras.Uma abordagem simples é observar amatrizcorrelacional(correlationmatrix),naqualaentradanalinhaienacolunajéacorrelaçãoentreasdimensõesi-ésimaej-ésimadosdados:

defcorrelation_matrix(data):"""retornaonum_columnsxnum_columnsmatrixcujaentrada(i,j)-ésimaéacorrelaçãoentreascolunasdedadosiej"""

_,num_columns=shape(data)

defmatrix_entry(i,j):returncorrelation(get_column(data,i),get_column(data,j))

returnmake_matrix(num_columns,num_columns,matrix_entry)

Umaabordagemmaisvisual(sevocênãotivermuitasdimensões)éfazerumamatriz de gráfico de dispersão (scatterplotmatrix— Figura 10-4) mostrandotodos os pareamentos dos gráficos de dispersão. Para fazer isso, usaremosplt.subplots(), que permite que criemos uma subparcela do nosso gráfico. Nósfornecemosonúmerodelinhasecolunas,eeleretornaumobjetofigure(quenãousaremos)eumarraybidimensionaldeobjetosaxes(cadaqualcomseugráfico):

importmatplotlib.pyplotasplt

_,num_columns=shape(data)fig,ax=plt.subplots(num_columns,num_columns)

foriinrange(num_columns):forjinrange(num_columns):

#dispersaacolumn_jnoeixoxversuscolumn_inoeixoyifi!=j:ax[i][j].scatter(get_column(data,j),get_column(data,i))

#amenosquei==j,emcujocasomostraonomedasérieelse:ax[i][j].annotate("série"+str(i),(0.5,0.5),

xycoords='axesfraction',ha="center",va="center")

#entãoescondeasetiquetasdoseixosexceto#osgráficosinferioresedaesquerdaifi<num_columns-1:ax[i][j].xaxis.set_visible(False)ifj>0:ax[i][j].yaxis.set_visible(False)

#consertaasetiquetasinferioresàdireitaesuperioresàesquerdadoseixos,#queestáerradopoisseusgráficossomentepossuemtextosax[-1][-1].set_xlim(ax[0][-1].get_xlim())

Page 185: Data Science do zero: Primeiras regras com o Python

ax[0][0].set_ylim(ax[0][1].get_ylim())

plt.show()

Figura10-4.MatrizdeGráficodeDispersão

Ao observar os gráficos de dispersão, você pode ver que a série 1 é muitonegativamente correlacionada com a série 0, a série 2 é positivamentecorrelacionadacomasérie1easérie3somenteaceitaosvalores0e6,com0correspondendoaosvaloresmenoresdasérie2e6correspondendoaosmaiores.

Essa é uma maneira rápida de ter uma ideia de como as suas variáveis sãocorrelacionadas(amenosquevocêpassehorasajustandomatplotlibparaexibirascoisasexatamentedojeitoquevocêquere,nessecaso,nãoémuitorápido).

Page 186: Data Science do zero: Primeiras regras com o Python

LimpandoeTransformandoOsdadosdomundorealsãosujos.Muitasvezes,vocêteráquetrabalharnelesantes de usá-los. Vimos alguns exemplos disso no Capítulo 9. Temos queconverterstringsparafloatsou intsantesdeusá-las.Anteriormente,fizemosissoumpoucoantesdeusarmososdados:

closing_price=float(row[2])

Masémenospropícioaoerrofazeraanálisenofluxodeentrada,oquepodemosfazer ao criar uma função que envolva csv.reader. Forneceremos uma lista deinterpretadores a ele, cada um especificando como analisar uma das colunas.UsaremosNonepararepresentar“nãofaçanadacomestacoluna”:

defparse_row(input_row,parsers):"""dadaumalistadeinterpretadores(algunspodemserNone)apliqueoapropriadoacadaelementodeinput_row"""

return[parser(value)ifparserisnotNoneelsevalueforvalue,parserinzip(input_row,parsers)]

defparse_rows_with(reader,parsers):"""envolveumreaderparaaplicarosinterpretadoresemcadaumadesuaslinhas"""forrowinreader:

yieldparse_row(row,parsers)

Esetiveralgumdadoruim?Umvalor“float”quenãorepresenteumnúmero?PreferiríamosreceberumNonedoquetravarnossoprograma.Podemosfazerissocomumafunçãoauxiliadora:

deftry_or_none(f):"""envolvefpararetornarNoneseflevantarumaexceçãopresumequefleveapenasumaentrada"""deff_or_none(x):

try:returnf(x)except:returnNone

returnf_or_none

depoisdissopodemosreescreverparse_rowparausá-lo:defparse_row(input_row,parsers):

return[try_or_none(parser)(value)ifparserisnotNoneelsevalue

Page 187: Data Science do zero: Primeiras regras com o Python

forvalue,parserinzip(input_row,parsers)]

Porexemplo,setivermosospreçosdasaçõesseparadosporvírgulascomdadosruins:

6/20/2014,AAPL,90.916/20/2014,MSFT,41.686/20/3014,FB,64.56/19/2014,AAPL,91.866/19/2014,MSFT,n/a6/19/2014,FB,64.34

podemoslereanalisaremumúnicopassoagora:importdateutil.parserdata=[]

withopen("comma_delimited_stock_prices.csv","rb")asf:reader=csv.reader(f)forlineinparse_rows_with(reader,[dateutil.parser.parse,None,float]):

data.append(line)

depoisdissosomenteprecisamoschecarporlinhasNone:forrowindata:

ifany(xisNoneforxinrow):printrow

e decidir o que queremos fazer com eles. (Demodo geral, as três opções sãojogá-losfora,voltarparaafontee tentarconsertarodadoruim/faltoso,ounãofazernadaecruzarosdedos.)

Poderíamos criar auxiliadores semelhantes para csv.DictReader. Nesse caso, vocêapenas teria que fornecer um dict de analisadores por meio de um nome decampo.Porexemplo:

deftry_parse_field(field_name,value,parser_dict):"""tentaanalisarovalorusandoafunçãoadequadaapartirdeparser_dict"""parser=parser_dict.get(field_name)#NonesenãotivertalentradaifparserisnotNone:

returntry_or_none(parser)(value)else:

returnvalue

defparse_dict(input_dict,parser_dict):return{field_name:try_parse_field(field_name,value,parser_dict)

forfield_name,valueininput_dict.iteritems()}

Uma próxima etapa seria checar por valores discrepantes, usando técnicas do

Page 188: Data Science do zero: Primeiras regras com o Python

“Explorando Seus Dados” na página 121 ou por investigação ad hoc. Porexemplo, você reparou que uma das datas no arquivo das ações tinha o ano3014?Issonãogeraránenhumerro(possivelmente),maséclaramenteerrado,evocêteráresultadosruinssevocênãoconsertá-lo.Osdadosdomundorealtêmpontos decimais faltosos, zeros extras, erros tipográficos e outros problemasincontáveiseéoseu trabalhocapturá-los.Talveznãosejaseu trabalhooficial,masquemmaisvaifazer?

Page 189: Data Science do zero: Primeiras regras com o Python

1.2.3.

1.2.

ManipulandoDadosUma das habilidades mais importantes de um cientista de dados émanipulardados. É mais uma visão geral do que uma técnica específica, portantotrabalharemoscomumgrupodeexemplosparamostrarumpouco.

Imaginequeestamostrabalhandocomdictsdospreçosdasaçõesqueseparecemcom:

data=[{'closing_price':102.06,'date':datetime.datetime(2014,8,29,0,0),'symbol':'AAPL'},#...

]

Conceitualmente,pensaremosnelescomolinhas(comoemumaplanilha).

Vamos começar perguntando sobre esses dados. Pelo caminho, perceberemospadrões no que estamos fazendo e abstrairemos algumas ferramentas parafacilitaramanipulação.

Por exemplo, suponha que queremos saber o preço mais alto para a AAPL.Vamossepararemetapasconcretas:

RestringiremlinhasAAPL.Pegaroclosing_pricedecadalinha.Levaromaxdetaispreços.

Podemosfazerostrêsdeumasóvezusandoumacompreensãodelista:max_aapl_price=max(row["closing_price"]

forrowindataifrow["symbol"]=="AAPL")

Commaisfrequência, talvezqueiramossaberopreçomaisaltoparacadaaçãoemnossoconjuntodedados.Umamaneiradefazeré:

Agrupartodasaslinhascomomesmosymbol(símbolo).Dentrodecadagrupo,fazeromesmodeantes:#agrupaaslinhasporsímbolo

Page 190: Data Science do zero: Primeiras regras com o Python

by_symbol=defaultdict(list)forrowindata:by_symbol[row["symbol"]].append(row)

#usaacompreensãododictparaencontraromaxparacadasímbolomax_price_by_symbol={symbol:max(row["closing_price"]

forrowingrouped_rows)forsymbol,grouped_rowsinby_symbol.iteritems()}

Jáexistemalgunspadrõesaqui.Nosdoisexemplos, tivemosquepuxarovalorclosing_priceparaforadecadadict.Entãovamoscriarumafunçãopararecolherumcampo de um dict, e outra função para arrancar esse mesmo campo de umacoleçãodedicts:

defpicker(field_name):"""retornaumafunçãoquerecolheumcampodeumdict"""returnlambdarow:row[field_name]

defpluck(field_name,rows):"""transformaumalistadedictsemumalistadevaloresfield_name"""returnmap(picker(field_name),rows)

Tambémpodemoscriarumafunçãoparaagruparaslinhaspeloresultadodeumafunçãogroupereaplicar,poropção,umtipodevalue_transformemcadagrupo:

defgroup_by(grouper,rows,value_transform=None):#achaveéasaídadegrouper,ovaloréumalistadelinhasgrouped=defaultdict(list)forrowinrows:

grouped[grouper(row)].append(row)

ifvalue_transformisNone:returngrouped

else:return{key:value_transform(rows)

forkey,rowsingrouped.iteritems()}

Issopermitequenósreescrevamososexemplosanterioresdeformasimples.Porexemplo:

max_price_by_symbol=group_by(picker("symbol"),data,lambdarows:max(pluck("closing_price",rows)))

Agorapodemoscomeçaraperguntarassuntosmaiscomplicados,comoquaissãoasmaiores emenoresmudançasdeporcentagemaodia emnosso conjuntodedados.Amudançanaporcentageméprice_today/price_yesterday–1,logoprecisamos

Page 191: Data Science do zero: Primeiras regras com o Python

1.2.3.

de alguma forma de associar o preço de hoje com o de ontem. Um métodopossíveléagruparospreçosporsímboloe,então,dentrodecadagrupo:

Ordenarospreçospordata.Usarzipparaterpares(anteriores,atuais).Transformarosparesemlinhasnovasde“mudançadepercentual”.

Começaremosescrevendoumafunçãoquefaçaotrabalhodentrodecadagrupo:defpercent_price_change(yesterday,today):

returntoday["closing_price"]/yesterday["closing_price"]-1

defday_over_day_changes(grouped_rows):#organizaaslinhaspordataordered=sorted(grouped_rows,key=picker("date"))

#compactacomumacompensaçãoparaterparesdediasconsecutivosreturn[{"symbol":today["symbol"],

"date":today["date"],"change":percent_price_change(yesterday,today)}

foryesterday,todayinzip(ordered,ordered[1:])]

Entãopodemosusá-locomoovalue_transformemumgroup_by:#achaveésymbol,ovaloréumachangededictschanges_by_symbol=group_by(picker("symbol"),data,day_over_day_changes)

#coletatodasaschangesdedictsparaumalistagrandeall_changes=[change

forchangesinchanges_by_symbol.values()forchangeinchanges]

Nesseponto,ficafácildeencontraromaioreomenor:max(all_changes,key=picker("change"))#{'change':0.3283582089552237,#'date':datetime.datetime(1997,8,6,0,0),#'symbol':'AAPL'}#see,e.g.http://news.cnet.com/2100-1001-202143.htmlmin(all_changes,key=picker("change"))#{'change':-0.5193370165745856,#'date':datetime.datetime(2000,9,29,0,0),#'symbol':'AAPL'}#vejaporexemplohttp://money.cnn.com/2000/09/29/markets/techwrap/

Podemosusaragoraoconjuntodedadosnovoall_changesparaencontrarqualmêsé o melhor para investir em ações tecnológicas. Primeiro, agrupamos asmudançaspormês;entãocomputamosamudançageraldentrodecadagrupo.

Page 192: Data Science do zero: Primeiras regras com o Python

Maisumavez,escrevemosumvalue_transformadequadoeusamosgroup_by:#paracombinarasmudançaspercentuais,adicionamos1acadaum,osmultiplicamos#esubtraímos1porexemplo,secombinarmos+10%e–20%,amudançageralé#(1+10%)*(1-20%)-1=1.1*.8-1=-12%defcombine_pct_changes(pct_change1,pct_change2):

return(1+pct_change1)*(1+pct_change2)-1

defoverall_change(changes):returnreduce(combine_pct_changes,pluck("change",changes))

overall_change_by_month=group_by(lambdarow:row['date'].month,all_changes,overall_change)

Faremos esses tipos de manipulações no decorrer do livro, geralmente semchamarmuitaatençãodiretaparaelas.

Page 193: Data Science do zero: Primeiras regras com o Python

RedimensionandoMuitastécnicassãosensíveisàescaladosseusdados.Porexemplo,imagineumconjunto de dados que consiste de altura e pesos de centenas de cientistas dedadosequevocêestátentandoidentificaroagrupamento(cluster)dostamanhosdoscorpos.

Intuitivamente, gostaríamos que os agrupamentos representassem pontospróximos uns aos outros, o que significa que precisamos de alguma noção dedistância entre eles. Nós já temos a função distance (distância) Euclideana,portantoumaabordagemnaturalseriatratarospares(altura,peso)comopontosemumespaçobidimensional.ObserveaspessoasnalistadaTabela10-1.

Tabela10-1.AlturasePesos

Semedirmosaalturaempolegadas,ovizinhomaispróximodeBéA:a_to_b=distance([63,150],[67,160])#10.77a_to_c=distance([63,150],[70,171])#22.14b_to_c=distance([67,160],[70,171])#11.40

Porém,semedirmosemcentímetros,ovizinhomaispróximodeBéC:a_to_b=distance([160,150],[170.2,160])#14.28a_to_c=distance([160,150],[177.8,171])#27.53b_to_c=distance([170.2,160],[177.8,171])#13.37

Éumaproblemáticaevidenteseamudançadasunidadesmudamosresultadosdessaforma.Poressemotivo,quandoasdimensõesnãosãocomparáveisumascom as outras, às vezes redimensionamos nossos dados a fim de que cadadimensão tenha média 0 e desvio padrão 1. Isso nos livra das unidades,convertendocadadimensãopara“desviospadrõesapartirdamédia”.

Apartirdessaexplicação,precisaremoscomputarameaneostandard_deviationparacadacoluna:

Page 194: Data Science do zero: Primeiras regras com o Python

defscale(data_matrix):"""retornaamédiaeosdesviospadrõesdecadacoluna"""num_rows,num_cols=shape(data_matrix)means=[mean(get_column(data_matrix,j))

forjinrange(num_cols)]stdevs=[standard_deviation(get_column(data_matrix,j))

forjinrange(num_cols)]returnmeans,stdevs

Eentãoosusaparacriarumanovamatrizdedados:defrescale(data_matrix):

"""redimensionaosdadosdeentradaparaquecadacolunatenhamédia0edesviopadrão1deixaintactascolunassemdesvio"""means,stdevs=scale(data_matrix)

defrescaled(i,j):ifstdevs[j]>0:return(data_matrix[i][j]-means[j])/stdevs[j]else:returndata_matrix[i][j]

num_rows,num_cols=shape(data_matrix)returnmake_matrix(num_rows,num_cols,rescaled)

Comosempre,vocêprecisautilizardeseubomsenso.Sevocêfossepegarumconjuntodedadosenormedealturasepesosefiltrá-lossomenteparaaspessoasquepossuíssementre69,5e70,5polegadas,seriabemprovável(dependendodaquestãoquevocêestejatentandoresponder)queavariaçãopermanecesseapenascomo um ruído e você poderia não querer colocar tal desvio padrão em umarelaçãodeigualdadecomosdesviosdasoutrasdimensões.

Page 195: Data Science do zero: Primeiras regras com o Python

ReduçãodaDimensionalidadeÀsvezes,asdimensões“reais”(ouúteis)dosdadospodemnãocorresponderàsdimensõesquetemos.Porexemplo,observeoconjuntodedadosnaFigura10-5.

Figura10-5.Dadoscomoseixos“errados”

Amaioriadasvariaçõesnosdadosparecemserdeumaúnicadimensãoquenãocorrespondeaoeixoxnemaoeixoy.

Quando esse é o caso, podemos usar uma técnica chamada de análise decomponentes principais para extrair uma ou mais dimensões que capturem amaiorvariaçãodosdadospossível.

Naprática,vocênãousariaessatécnicaemumconjuntodedadoscomodimensionaltãobaixo.Areduçãodedimensionalidadeémaisútilquandoseuconjuntodedadospossuiumgrandenúmerodedimensõesevocêquerencontrarumasubparcelaquecapturaamaiorpartedavariação.Infelizmente,essecasoédifícildeserilustradoem

Page 196: Data Science do zero: Primeiras regras com o Python

livrocomformatobidimensional.

Naprimeiraetapa,precisaremos transformarosdadosparaquecadadimensãotenhamédiazero:

defde_mean_matrix(A):"""retornaoresultadodesubtrairdecadavaloremAovalordamédiadasuacoluna.amatrizresultantetemmédia0emcadacoluna"""nr,nc=shape(A)column_means,_=scale(A)returnmake_matrix(nr,nc,lambdai,j:A[i][j]-column_means[j])

(Senãofizermosisso,épossívelquenossastécnicasidentificarãoamédiaemsiemvezdeidentificaravariaçãonosdados.)

AFigura10-6mostraosexemplosdedadosapósodescontodamédia.

Figura10-6.Dadosapósodescontodamédia

Agora, dada umamatriz descontada demédiaX, podemos perguntar: qual é a

Page 197: Data Science do zero: Primeiras regras com o Python

direçãoquecapturaamaiorvariaçãonosdados?

Especificamente,dadaumadireçãod(umvetordemagnitude1),cadalinhaxnamatrizseestendedot(x,d)nadireçãoded.Cadavetornão-zerowdeterminaumadireçãoseosredimensionarmosparatermagnitude1:

defdirection(w):mag=magnitude(w)return[w_i/magforw_iinw]

Portanto, dado um vetor não-zerow, podemos computar a variância do nossoconjuntodedadosnadireçãodeterminadaporw:

defdirectional_variance_i(x_i,w):"""avariâncianalinhax_inadireçãodeterminadaporw"""returndot(x_i,direction(w))**2

defdirectional_variance(X,w):"""avariânciadosdadosnadireçãodeterminadaporw"""returnsum(directional_variance_i(x_i,w)

forx_iinX)

Gostaríamosdeencontraradireçãoquemaximizaessavariância.Podemosfazerissousandoogradientedescente,assimquetivermosafunçãogradiente:

defdirectional_variance_gradient_i(x_i,w):"""acontribuiçãodalinhax_1paraogradientedavariânciadadireçãow"""projection_length=dot(x_i,direction(w))return[2*projection_length*x_ijforx_ijinx_i]

defdirectional_variance_gradient(X,w):returnvector_sum(directional_variance_gradient_i(x_i,w)

forx_iinX)

O componente principal é somente a direção que maximiza a funçãodirectional_variance:

deffirst_principal_component(X):guess=[1for_inX[0]]unscaled_maximizer=maximize_batch(

partial(directional_variance,X),#agoraéumafunçãodewpartial(directional_variance_gradient,X),#agoraéumafunçãodewguess)

returndirection(unscaled_maximizer)

Ou,sevocêpreferirogradientedescendenteestocástico:#aquinãohá“y”,entãopassamosumvetordeNones

Page 198: Data Science do zero: Primeiras regras com o Python

#efunçõesqueignoramaquelaentradadeffirst_principal_component_sgd(X):

guess=[1for_inX[0]]unscaled_maximizer=maximize_stochastic(

lambdax,_,w:directional_variance_i(x,w),lambdax,_,w:directional_variance_gradient_i(x,w),X,[Nonefor_inX],#o"y"falsoguess)

returndirection(unscaled_maximizer)

Noconjuntodedadosdescontadodamédia, isso retornaadireção [0.924, 0.383],quepareceparacapturaroeixoprimárioaolongodoqualavariaçãodosdados(Figura10-7).

Figura10-7.Oprimeirocomponenteprincipal

Uma vez que achamos a direção, que é o principal componente, podemosprojetarnossosdadosparaencontrarosvaloresdaquelecomponente:

defproject(v,w):

Page 199: Data Science do zero: Primeiras regras com o Python

"""retornaaprojeçãodevnadireçãow"""projection_length=dot(v,w)returnscalar_multiply(projection_length,w)

Se quisermos encontrar componentes mais distantes, primeiro temos queremoverasprojeçõesapartirdosdados:

defremove_projection_from_vector(v,w):"""projetavemwesubtraioresultadodev"""returnvector_subtract(v,project(v,w))

defremove_projection(X,w):"""paracadalinhadeXprojetaalinhaemw,esubtraioresultadodalinha"""return[remove_projection_from_vector(x_i,w)forx_iinX]

Comoesseexemplodeconjuntodedadosébidimensional,apósremovermosoprimeirocomponente,oquesobraseráefetivamenteunidimensional(Figura10-8).

Figura10-8.Dadosapósaremoçãodaprimeiracomponenteprincipal

Page 200: Data Science do zero: Primeiras regras com o Python

Nesse ponto, podemos encontrar o próximo componente principal ao repetir oprocessosobreoresultadoremove_projection(Figura10-9).

Em um conjunto de dados de dimensão mais alta, podemos encontrar tantoscomponentesquantoquisermos:

defprincipal_component_analysis(X,num_components):components=[]for_inrange(num_components):

component=first_principal_component(X)components.append(component)X=remove_projection(X,component)

returncomponents

Podemos então transformar nossos dados no espaço de dimensão mais baixacobertopeloscomponentes:

deftransform_vector(v,components):return[dot(v,w)forwincomponents]

deftransform(X,components):return[transform_vector(x_i,components)forx_iinX]

Essatécnicaévaliosapordoismotivos.Primeiro,elapodenosajudaralimparnossosdadosaoeliminardimensõesquesãomeroruídoeconsolidardimensõesquesãoaltamentecorrelacionadas.

Page 201: Data Science do zero: Primeiras regras com o Python

Figura10-9.Osprimeirosdoiscomponentesprincipais

Segundo, após extrair uma representação de dimensãomais baixa dos nossosdados, podemos usar uma variedade de técnicas que não funcionam bem emdadosdealtadimensão.Veremosalgunsexemplosde tais técnicasnodecorrerdolivro.

Aomesmotempo,enquantopodeajudaraconstruirmodelosmelhores,tambémpodetorná-losmaisdifíceisdesereminterpretados.Éfácilentenderconclusõescomo“cadaanoextradeexperiênciaadicionaumamédiade$10knosalário”.Émais difícil de entender que “cada aumento de 0,1 no terceiro componenteprincipaladicionaumamédiade$10knosalário”.

Page 202: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosComomencionamosnofinaldoCapítulo9,pandas(http://pandas.pydata.org/)provavelmenteéaferramentaprimáriadePythonparalimpar,transformar,manipularetrabalharcomdados.Todososexemplosquefizemosàmãonestecapítulopoderiamtersidofeitoscommaissimplicidadeusandopandas.PythonforDataAnalysis(O’Reilly)éamelhormaneiradeaprenderpandas.scikit-learnpossuiumagrandevariedadedefunçõesdedecomposiçãodematriz(http://bit.ly/1ycOLJd),incluindoanálisedecomponenteprincipal(PrincipalComponentAnalyses–PCA).

Page 203: Data Science do zero: Primeiras regras com o Python

CAPÍTULO11

AprendizadodeMáquina

Estousempredispostoaaprenderapesardenemsempregostardeserensinado.—WinstonChurchill

Muitas pessoas imaginamque data science é, emmaior parte, aprendizado demáquinaequeoscientistasdedadosconstroem,praticameajustammodelosdeaprendizadodemáquinaodia inteiro.E,novamente,muitasdesaspessoasnãosabem o que é aprendizado de máquina. Na verdade, data science é maistransformarproblemasempresariaisemproblemasdedadosecoletar,entender,limpareformatarosdados,apósoqueaprendizadodemáquinaépraticamenteuma consideração subsequente.Mesmo assim, é uma referência interessante eessencialquevocêdevesaberafimdepraticardatascience.

Page 204: Data Science do zero: Primeiras regras com o Python

ModelagemAntesdepodemosfalarsobreoaprendizadodemáquina,precisamosfalarsobremodelos.

Oqueéummodelo?Ésimplesmenteaespecificaçãodeumarelaçãomatemática(ouprobabilística)existenteentrevariáveisdiferentes.

Por exemplo, se você está tentado a levantar dinheiro para o seu site de redesocial, talvezvocêprecise deummodelodenegócios (possivelmente emumaplanilha) que receba entradas como “número de usuários”, “rendimento depropaganda por usuário” e “número de funcionários” e exiba como saída seulucroanualpelospróximosanos.Umlivrodereceitas implicaummodeloquerelacionaentradascomo“númerodecomensais”e“apetite”paraasquantidadesdos ingredientes necessários. E, se você já assistiu pôquer na televisão, vocêsabequeelesestimama“probabilidadedeganhar”decada jogadorem temporeal baseado em um modelo que leva em consideração as cartas que foramreveladasatéentãoeadistribuiçãodecartasnobaralho.

O modelo de negócios é, provavelmente, baseado em relações simples dematemática:lucroéorendimentomenosasdespesas,orendimentoésomadasunidadesvendidasvezesopreçomédioeassimpordiante.Omodelodolivrodereceitas é baseado em tentativas e erros — alguém foi na cozinha eexperimentou combinações diferentes de ingredientes até encontrar uma quegostasse.Omodelodopôquerébaseadonateoriadaprobabilidade,asregrasdopôquer e algumaspremissas razoavelmente inócuos sobre o processo aleatóriopeloqualascartassãodistribuídas.

Page 205: Data Science do zero: Primeiras regras com o Python

••••

OQueÉAprendizadodeMáquina?Todo mundo possui sua própria definição, mas usaremos aprendizado demáquina para nos referir à criação e aousodemodelos que são aprendidosapartir dos dados. Em outros contextos isso pode ser chamado de modelopreditivooumineraçãodedados,masvamosmanteroaprendizadodemáquina.Normalmente, nosso objetivo será usar os dados existentes para desenvolvermodelosquepossamosusarparaprever possíveis saídas para os dadosnovos,como:

Preverseumamensagemdee-mailéspamounãoPreverseumatransaçãodocartãodecréditoéfraudulentaPreverqualaprobabilidadedeumcompradorclicaremumapropagandaPreverqualtimedefutebolganharáoSuperBowl

Consideraremososmodelos supervisionados (nosquais existeumconjuntodedados etiquetados com a resposta correta para aprendizagem) e modelos semsupervisão (nos quais não existem tais etiquetas). Existem vários outros tiposcomo semisupervisionados (nos quais apenas alguns dados são etiquetados) eonline(nosquaisomodeloprecisaterumajustecontínuoemfacedachegadadenovosdados),masnãoserãoabordadosnestelivro.

Agora, até mesmo na situação mais simples existe um universo inteiro demodelos que podem descrever a relação na qual estamos interessados. Namaioria dos casos, nós mesmos escolheremos uma família parametrizada demodeloseentãousaremososdadosparaaprenderparâmetrosquesão,decertaforma,ótimos.

Porexemplo,podemospresumirqueaalturadeumapessoaé(maisoumenos)umafunçãolineardoseupesoeentãousarosdadosparadescobrirqualfunçãolinear é essa. Ou, podemos presumir que uma árvore de decisão é uma boamaneirade identificarquaisdoençasnossospacientespossuemeentãousarosdados para descobrir a ótima árvore de decisão. Pelo restante do livroinvestigaremosfamíliasdiferentesdemodelosquepodemosaprender.

Masantesdisso,precisamosentendermelhorosfundamentosdoaprendizadode

Page 206: Data Science do zero: Primeiras regras com o Python

máquina.Pelorestodocapítulo,discutiremosalgunsconceitosbásicosantesdechegarmosnosmodelospropriamenteditos.

Page 207: Data Science do zero: Primeiras regras com o Python

SobreajusteeSub-AjusteUmperigocomumemaprendizadodemáquinaéosobreajuste—produzirummodelo de bomdesempenho comos dados que você treina,mas que não lidemuitobemcomnovosdados.

Issopodeimplicaroaprendercombasenoruídodosdados.Ou,podeimplicarem aprender a identificar entradas específicas em vez de qualquer fator quesejamdefatopreditivosdasaídadesejada.

Ooutro ladoéosub-ajuste,produzindoummodeloquenãodesempenhabemnemcomosdadosusadosnotreino,apesardeque,quandoaconteceisso,vocêdecide que seu modelo não é bom o suficiente e continua a procurar pormelhores.

Figura11-1.Sobreajusteesub-ajuste

Page 208: Data Science do zero: Primeiras regras com o Python

Na Figura 11-1, encaixei três polinômios em uma amostra de dados. (Não sepreocupeemcomo,chegaremoslánoscapítulosposteriores.)

A linha horizontal mostra o melhor grau adequado polinomial 0 (isto é,constante).Elesub-ajustaodadoemtreinamentointensamente.Omelhorajusteporumpolinômiodo9ºgrau(istoé,parâmetro10)passaportodosospontosdedadosemtreinamento,massobreajustagravemente—sefossemosadquirirumpoucomaisdepontosdedados,provavelmentesairiambemerrados.Ea linhade1ºgrautemumbomequilíbrio—ébempróximoacadaponto,e(seessesdados são representativos) a linha estará próxima dos novos pontos de dadostambém.

Evidentemente, osmodelosque sãomuito complexos tendemao sobreajuste enãolidambemcomdadosalémdaquelescomosquaisforamtreinados.Então,como temoscertezaquenossosmodelosnãosãomuitocomplexos?Ométodomais fundamental envolve o uso de dados diferentes para treinar e testar omodelo.

Amaneiramaisfácildefazerissoédividirseuconjuntodedados,afimdeque,porexemplo,doisterçosdelesejamusadosparatreinaromodeloedepoismedirodesempenhodomodelocomaparterestante:

defsplit_data(data,prob):"""divideosdadosemfrações[prob,1–prob]"""results=[],[]forrowindata:

results[0ifrandom.random()<probelse1].append(row)returnresults

Comfrequência,teremosumamatrizxdevariáveisdeentradaeumvetorydevariáveisde saída.Nesse caso,precisamosnos certificarde colocarosvalorescorrespondentestantonosdadosemtreinamentocomonosdadosdeteste:

deftrain_test_split(x,y,test_pct):data=zip(x,y)#pardevalorescorrespondentestrain,test=split_data(data,1-test_pct)#divideoconjuntodeparesdedadosx_train,y_train=zip(*train)#truquemágicodeun-zip(descompactação)x_test,y_test=zip(*test)returnx_train,x_test,y_train,y_test

afimdefazeralgocomo:model=SomeKindOfModel()

Page 209: Data Science do zero: Primeiras regras com o Python

x_train,x_test,y_train,y_test=train_test_split(xs,ys,0.33)model.train(x_train,y_train)performance=model.test(x_test,y_test)

Se omodelo foi sobreajustado para os dados em treinamento, então ele devedesempenharmalsobreosdadosdeteste(completamenteseparados).Deoutramaneira,seeledesempenhabemsobreosdadosdeteste,entãovocêpodeficarmaisconfiantequeeleestáajustadoemvezdesobreajustado.

Porém,existemduasformasdedartudoerrado.

Aprimeiraéseexistirempadrõescomunsaosdadosde testeede treinamentoquenãoseriamgeneralizadosemumconjuntomaiordedados.

Por exemplo, imagine que seu conjunto de dados consiste da atividade dousuário,umalinhaporusuárioporsemana.Emtalcaso,amaioriadosusuáriosapareceráemambososdadosdetesteedetreinamentoealgunsmodelostalvezaprendessemaidentificarosusuáriosemvezdedescobrirrelaçõesenvolvendoatributos.Nãoédegrandepreocupação,apesarde teracontecidocomigoumavez.

Umproblemamaioré sevocêusaradivisãode testes/treinamentonãoapenaspara avaliar ummodelomas, também, para escolher entre os váriosmodelos.Nesse caso, embora cada modelo individual possa não ser sobreajustado, o“escolha ummodelo que desempenhemelhor nos dados de teste” é umquasetreinamentoque faz comqueoconjuntode testes funcionecomoumsegundoconjuntodetreinamento.(Éclaroqueomodeloquetivermelhordesempenhonotesteteráummelhordesempenhonoconjuntodeteste.)

Emumasituaçãocomoessa,vocêdeveriadividirosdadosem trêspartes:umconjuntodetreinamentoparaconstruirmodelos,umconjuntodevalidaçãoparaescolherentreosmodelostreinadoseumconjuntodetesteparaavaliaromodelofinal.

Page 210: Data Science do zero: Primeiras regras com o Python

PrecisãoQuandonãoestoupraticandodata science,eumeaventuronamedicina.E,nomeutempolivre,euinventeiumtestesimples,não-invasivoquepodeserfeitocomumrecém-nascidoqueprediz—comumaprecisãomaiorque98%—seorecém-nascidodesenvolverá leucemia.Meu advogadome convenceude queoteste não é patenteável, portanto compartilharei com você os detalhes aqui:preveraleucemiaseesomenteseonomedobebêforLuke(quesepareceumpoucocomosomdeleukemia,leucemiaeminglês).

Como veremos a seguir, esse teste possui mesmo mais de 98% de precisão.Apesardisso,éumtesteincrivelmenteestúpidoeumaboailustraçãodomotivopeloqualnósnãousamos“precisão”paramediraeficiênciadeummodelo.

Imagine construir ummodelopara fazer uma avaliaçãobinária. Esse e-mail éspam? Deveríamos contratar este candidato? Este viajante é um terrorista emsegredo?

Dadoumconjuntodedadosetiquetadoseummodelopreditivo,cadapontodedadosseestabeleceemquatrocategorias:

Positivoverdadeiro:“Estamensageméspameprevimosspamcorretamente.”Positivofalso(ErroTipo1):“Estamensagemnãoéspam,masprevimosqueera.”Negativofalso(ErroTipo2):“Estamensageméspam,masprevimosquenãoera.”Negativoverdadeiro:“Estamensagemnãoéspameprevimosquenãoera.”

Representamosessacontagememumamatrizdeconfusão:

Spam NãoéSpam

premissa“Spam”

PositivoVerdadeiro

PositivoFalso

Page 211: Data Science do zero: Primeiras regras com o Python

premissa“NãoéSpam”

NegativoFalso

NegativoVerdadeiro

Vamosver comomeu teste de leucemia se encaixanessa estrutura.Por agora,aproximadamente 5 bebês de 1000 se chamamLuke (http://bit.ly/1CchAqt). Aincidênciade sempreda leucemiaédeaproximadamente1,4%,ou14decada1000pessoas(http://1.usa.gov/1ycORjO).

Se acreditarmos que esses dois fatores são independentes e aplicar meu teste“Lukepara leucemia”emummilhãodepessoas,esperaríamosverumamatrizdeconfusãocomesta:

Podemosusarissoentãoparacomputardiversasestatísticassobreodesempenhodo modelo. Por exemplo, a acurácia é definida como a fração de premissascorretas:

defaccuracy(tp,fp,fn,tn):correct=tp+tntotal=tp+fp+fn+tnreturncorrect/total

printaccuracy(70,4930,13930,981070)#0.98114

Pareceumnúmerobeminteressante.Mas,evidentemente,nãoéumbomteste,oquesignificaquenãodeveríamoscolocarmuitacrençanaacuráciabruta.

É comum considerar a combinação de precisão e sensibilidade. Exatidãosignificaoquãoprecisasnossasprevisõespositivaseram:

defprecision(tp,fp,fn,tn):returntp/(tp+fp)

printprecision(70,4930,13930,981070)#0.014

Asensibilidademedequalfraçãodospositivosnossosmodelosidentificam:defrecall(tp,fp,fn,tn):

returntp/(tp+fn)

Page 212: Data Science do zero: Primeiras regras com o Python

printrecall(70,4930,13930,981070)#0.005

Ambossãonúmerosterríveis,refletindoummodeloterrível.

Àsvezes,precisãoesensibilidadesãocombinadosaoF1Score,definidoassim:deff1_score(tp,fp,fn,tn):

p=precision(tp,fp,fn,tn)r=recall(tp,fp,fn,tn)

return2*p*r/(p+r)

Essa é a média harmônica (http://en.wikipedia.org/wiki/Harmonic_mean) daacuráciaesensibilidadeeseachanecessariamenteencontradaentreelas.

Geralmente, a escolha de um modelo implica em um compromisso entreacuráciaesensibilidade.Ummodeloqueprevê“sim”quandoseestáumpoucoconfianteprovavelmenteteráumasensibilidadealtamasumaacuráciabaixa;ummodelo que prevê “sim” somente quando está extremamente confianteprovavelmenteteráumasensibilidadebaixaeumaacuráciaalta.

Como alternativa, você pode pensar nisso como uma troca entre positivos enegativosfalsos.Dizer“sim”commuitafrequênciatrarámuitospositivosfalsos;dizer“não”commuitafrequênciatrarámuitosnegativosfalsos.

Imaginequeexistiramdezfatoresderiscoparaa leucemiaeque,quantomaisvocêos tenha,maisvocêestarápropensoadesenvolver leucemia.Nessecaso,vocêpodeimaginarumacontinuidadedetestes:“preverleucemiasehouveraomenosumfatorderisco”,“preverleucemiasehouveraomenosdoisfatoresderisco”eassimpordiante.Conformeolimiteaumenta,vocêaumentaaexatidãodoteste(desdequeaspessoascommaisfatoresderiscosejammaispropensasadesenvolveradoença)ediminuiaconfirmaçãodoteste(umavezquecadavezmenospacientesdadoençachegarãoaolimite).Emcasoscomoesse,escolherolimitecertoéumaquestãodeencontrarocompromissocerto.

Page 213: Data Science do zero: Primeiras regras com o Python

CompromissoentrePolarizaçãoeVariânciaOutramaneira de pensar sobre o problema de sobreajuste é um compromissoentrepolarizaçãoevariância.

Ambas são medidas do que aconteceria se você fosse treinar seu modelonovamentemuitas vezes em diferentes conjuntos de dados de treinamento (damesmapopulação).

Por exemplo, o modelo polinomial de grau 0 em “Sobreajustando e Sub-Ajustando” na página 142 cometerá muitos erros para qualquer conjunto dedados em treinamento (tirados da mesma população), o que significa que elepossui uma polarização alta. Porém, quaisquer dois conjuntos de treinamentoescolhidos aleatoriamente deveriam fornecer modelos similares (uma vez quequaisquerdoisconjuntosdetreinamentoescolhidosaleatoriamentedeveriamtervalores médios bem similares). Então dizemos que ele possui uma baixavariância. Polarização alta e variância baixa geralmente pertencem ao sub-ajuste.

Por outro lado, o modelo polinomial de grau 9 se encaixa no conjunto detreinamento comperfeição.Possui polarizaçãobaixa,masvariânciamuito alta(quaisquer dois conjuntos em treinamento dariam origem a modelos bemdiferentes).Elescorrespondemaosobreajuste.

Pensarsobreproblemasdemodelosdessaformapodeajudaradescobriroquefazerquandoseumodelonãofuncionamuitobem.

Seoseumodelopossuiapolarizaçãoalta(oquesignificaqueelenãopossuiumbom desempenho no seu conjunto em treinamento), algo mais a tentar éadicionar mais características. Ir do modelo polinomial de grau 0 em“SobreajustandoeSub-Ajustando”napágina142paraomodelopolinomialdegrau1foiumagrandemelhoria.

Seoseumodelo temvariânciaalta,entãovocêpodedemodosimilarremovercaracterísticas.Masoutrasoluçãoseriaobtermaisdados(sepuder).

Page 214: Data Science do zero: Primeiras regras com o Python

Figura11-2.Reduzindoavariânciacommaisdados

NaFigura11-2,ajustamosumpolinômiodegrau9paradiferentesamostras.Omodelo ajustado com base nos 10 pontos de dados está em todo lugar, comovimosanteriormenteSe treinássemoscom100pontosdedados,haveriamuitomenos sobreajuste. E omodelo treinado a partir dos 1000 pontos de dados émuitoparecidocomomodelodegrau1.

Mantendo uma constante na complexidade domodelo, quantomais dados há,maisdifíciléparasobreajustar.

Poroutrolado,maisdadosnãoajudacomapolarização.Seseumodelonãousarecursos suficientespara capturar regularidadesnosdados, colocarmaisdadosnãoajudará.

Page 215: Data Science do zero: Primeiras regras com o Python

•••

RecursosExtraçãoeSeleçãodeCaracterísticaComo mencionamos, quando os seus dados não tiverem característicassuficientes,épossívelqueseumodelosub-ajuste.Equandoseusdadospossuemmuitascaracterísticas,ficafácildesobreajustar.Masoquesãocaracterísticasedeondeelasvêm?

Característicassãoquaisquerentradasquefornecemosaonossomodelo.

Nocasomaissimples,ascaracterísticassãofornecidasapenasavocê.Sevocêquiserprevero saláriode alguémbaseadoemseus anosde experiência, entãoanosdeexperiênciaéaúnicacaracterísticaquevocêpossui.

(Apesardeque,comovimosem“SobreajusteeSub-Ajuste”napágina142,vocêtambémpodeconsideraradicionaranoscomoexperiênciaaoquadrado,aocubo,eassimpordianteseissoajudaraconstruirummodelomelhor.)

As coisas ficam mais interessantes conforme seus dados ficam maiscomplicados. Imagine tentar construir um filtro de spampara prever se um e-mailélixoounão.Amaioriadosmodelosnãosaberáoquefazercomoe-mailem si, cru, que é apenas uma coleção de texto. Você terá que extrair ascaracterísticas.Porexemplo:

Essee-mailcontémapalavra“Viagra”?Quantasvezesaletradaparece?Qualeraodomíniodoremetente?

A primeira é somente sim ou não, o que remete a 1 ou 0. A segunda é umnúmeroeaterceiraéumaescolhadeumlequedeopções.

Quasesempre,extrairemoscaracterísticasdosnossosdadosquecairãoemumadessas três categorias. Além do mais, o tipo de características que temosrestringeotipodemodelosquepodemosusar.

O classificadorNaiveBayes que construiremos noCapítulo13 é destinado àscaracterísticassim-ou-não,comooprimeironalistaanterior.

Modelos de regressão, como estudaremos nos Capítulos 14 e 16, requerem

Page 216: Data Science do zero: Primeiras regras com o Python

característicasnuméricas(incluindovariáveispostiças(dummy)de0se1s).

E as árvores de decisão, as quais veremos no Capítulo 17, podem lidar comdadosnuméricosoucategóricos.

Apesardetentarmoscriarcaracterísticasnoexemplodofiltrodespam,algumasvezestentaremosremovê-las.

Por exemplo, suas entradas podem ser vetores de várias centenas de números.Dependendo da situação, talvez seja apropriado diminuir para apenas asdimensões importantes (como em “Redução da Dimensionalidade” na página134) e usar somente um número pequeno de características. Ou talvez seriaapropriadousarumatécnica(comoregularizaçãoem“Regularização”napágina186)quepenalizaosmodelosquantomaiscaracterísticaselesusam.

Como escolhemos essas características? Aqui é onde uma combinação deexperiênciaedomíniodeentendimentoentraemjogo.Sevocêrecebemuitose-mails, logo é possível que você tenha percebido a presença de certas palavrascomoum indicador de spam.Tambémpode ter percebido que o número de dpodenãoserumindicadordespam.Mas,nogeral,vocêteráquetentarmétodosdiferentes,oquefazpartedadiversão.

Page 217: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosContinuelendo!Ospróximoscapítulossãosobrefamíliasdiferentesdemodelosdeaprendizadodemáquina.OcursoMachineLearningdaCourseraéoMOOC(doinglêsMassiveOpenOnlineCourse)originaleéumbomlugarparaumentendimentomaisprofundosobreaprendizadodemáquina.OMOOCMachineLearningdaCaltechtambémébom.TheElementsofStatisticalLearningéumlivrodidáticocanônicoquepodeserbaixadogratuitamente(http://stanford.io/1ycOXbo).Masestejaavisado:temmuitamatemática.

Page 218: Data Science do zero: Primeiras regras com o Python

CAPÍTULO12

K–VizinhosMaisPróximos

Sevocêquiserperturbarseusvizinhos,digaaverdadesobreeles.—PietroAretino

Imaginequevocêestátentandoprevercomoeuvouvotarnaspróximaseleiçõespresidenciais.Sevocênãosabemaisnadasobremim(esevocêtiverosdados),umaabordagemlógicaéconsiderarcomomeusvizinhosestãoplanejandovotar.Como eumoro no centro deSeattle,meus vizinhos estão planejando votar nocandidato democrata, o que sugere que o “candidato Democrata” é um bompalpitepramimtambém.

Agora, imaginequevocêsaibamaissobremimdoquesomenteondeeumoro—talvezvocêsaibaminhaidade,meusrendimentos,quantosfilhoseutenho,eassim por diante.Considerando que omeu comportamento é influenciado (oucaracterizado)portaiscoisasaomáximo,considerarapenasmeusvizinhosqueestãopróximosdemimemtodasessasdimensõespareceserumaclassificaçãomelhor do que considerar todos os meus vizinhos. Essa é a ideia por trás daclassificaçãodosvizinhosmaispróximos.

Page 219: Data Science do zero: Primeiras regras com o Python

••

OModeloOs vizinhos mais próximos é um dos modelos preditivos mais simples queexiste. Ele não possui premissas matemáticas e não requer nenhum tipo demaquináriopesado.Eleapenasrequer:

UmanoçãodedistânciaUmapremissadequepontosqueestãopertoumdooutrosãosimilares

Amaioriadastécnicasqueveremosnestelivroconsideramoconjuntodedadoscomoumtodoafimdeaprenderpadrõesnosdados.Osvizinhosmaispróximos,por outro lado, rejeitam muitas informações conscientemente, uma vez que aprevisão para cada ponto novo depende somente de alguns pontos maispróximos.

Mais ainda, os vizinhos mais próximos provavelmente não vão lhe ajudar aentenderosfatoresdeterminantesdequaisquerfenômenososquaisvocêestejaconsiderando.Preverosmeusvotosbaseadosnosvotosdosmeusvizinhosnãolhedizmuitosobreoquemefazvotardomeujeito,enquantoquealgummodeloalternativo que prevê meu voto baseado (digamos) no meu salário e no meuestadociviltalvezpossadizer.

Emumasituaçãogeral,temosalgunspontosdedadoseumconjuntoderótuloscorrespondentes. Os rótulos podem ser True e False, indicando se cada entradasatisfaz algumas condições como “é spam?” ou “é venenoso?” ou “seriaprazeroso assistir?” Ou eles poderiam ser categorias, como classificaçõesindicativasde filmes (L, 10, 12, 14, 16, 18).Ouelespoderiam sernomesdoscandidatos à presidência. Ou eles poderiam ser linguagens de programaçãopreferidas.

Nonossocaso,ospontosdedadosserãovetores,oquesignificaquepodemosusarafunçãodistancedoCapítulo4.

Digamosqueescolhemosumnúmerokcomo3ou5.Então,quandoqueremosclassificar alguns novos pontos de dados, encontramos os pontos rotulados kmaispróximoseosdeixamosvotarnanovasaída.

Page 220: Data Science do zero: Primeiras regras com o Python

•••

Para fazer isso, precisaremos de uma função que conte os votos. Umapossibilidadeé:

defraw_majority_vote(labels):votes=Counter(labels)winner,_=votes.most_common(1)[0]returnwinner

Masissonãofaznadadeinteligentecomasrelações.Porexemplo,imaginequeestamos classificando os filmes e os cinco filmes mais próximos sãoclassificados em L, L, 10, 10, 12. L e 10 têm dois votos. Nesse caso, temosváriasopções:

Escolherumdosvencedoresaleatoriamente.Ponderarosvotosàdistânciaeescolherovencedormaisvotado.Reduzirkatéencontrarmosumvencedorúnico.

Implementaremosoterceiro:defmajority_vote(labels):

"""presumequeasetiquetassãoordenadasdomaispróximoparaomaisdistante"""vote_counts=Counter(labels)winner,winner_count=vote_counts.most_common(1)[0]num_winners=len([count

forcountinvote_counts.values()ifcount==winner_count])

ifnum_winners==1:returnwinner#vencedorúnico,entãoodevolve

else:returnmajority_vote(labels[:-1])#tentanovamentesemomaisdistante

Écertezaque essemétodo funcionará emalgummomento, já quenapior dashipótesesreduziríamosparasomenteumrótuloe,nessecaso,elevence.

Comessafunçãoéfácilcriarumclassificador:defknn_classify(k,labeled_points,new_point):

"""cadapontorotuladodeveriaserumpar(point,label)"""

#organizaospontosrotuladosdomaispróximoparaomaisdistanteby_distance=sorted(labeled_points,

key=lambda(point,_):distance(point,new_point))

#encontraosrótulosparaoskmaispróximosk_nearest_labels=[labelfor_,labelinby_distance[:k]]

Page 221: Data Science do zero: Primeiras regras com o Python

#eosdeixavotarreturnmajority_vote(k_nearest_labels)

Vamosvercomoissofunciona.

Page 222: Data Science do zero: Primeiras regras com o Python

Exemplo:LinguagensFavoritasOresultadodaprimeirapesquisadeusuáriosdaDataSciencesterestádevolta,edescobrimos as linguagens de programação preferidas dos nossos usuários emalgumascidadesgrandes:

#cadaentradaé([longitude,latitude],favorite_language)

cities=[([-122.3,47.53],"Python"),#Seattle([-96.85,32.85],"Java"),#Austin([-89.33,43.13],"R"),#Madison#…eassimpordiante

]Ovice-presidentedoEnvolvimentoComunitárioquersabersepodemosusaressesresultadosparapreveralinguagemdeprogramaçãopreferidaparalugaresquenãofizerampartedapesquisa.

Comosempre,umprimeirobompassoédemarcarosdados(Figura12-1):#achaveéalinguagem,ovaloréopar(longitudes,latitudes)plots={"Java":([],[]),"Python":([],[]),"R":([],[])}

#queremosquecadalinguagemtenhamarcadorecordiferentesmarkers={"Java":"o","Python":"s","R":"^"}colors={"Java":"r","Python":"b","R":"g"}

for(longitude,latitude),languageincities:plots[language][0].append(longitude)plots[language][1].append(latitude)

#criaumasériededispersãoparacadalinguagemforlanguage,(x,y)inplots.iteritems():

plt.scatter(x,y,color=colors[language],marker=markers[language],label=language,zorder=10)

plot_state_borders(plt)#fingequetemosumafunçãoquefaçaisso

plt.legend(loc=0)#deixamatplotlibescolherolocalplt.axis([-130,-60,20,55])#ajustaoseixos

plt.title("LinguagensdeProgramaçãoPreferidas")plt.show()

Page 223: Data Science do zero: Primeiras regras com o Python

1.

2.

3.

Figura12-1.Linguagensdeprogramaçãopreferidas

Vocêdeveternotadoachamadaparaplot_state_borders(),umafunçãoqueaindanãodefinimos. Há uma implementação na página do livro no GitHub(http://bit.ly/1ycP2M8),eéumbomexercícioparatentarfazersozinho:

Procure na internet por algo como fronteiras dos estadoslatitudelongitude.Converta quaisquer dados que você encontrar emuma lista desegmentos[(long1,lat1),(long2,lat2)].Useplt.plot()paradesenharossegmentos.

Jáqueoslugaresmaispertotendemagostardamesmalinguagem,osk-vizinhosmaispróximosparecemserumaboaescolhaparaummodelopreditivo.

Para começar, vamos ver o que acontece se tentarmos prever a linguagempreferidadecadacidadeusandoseusvizinhosemvezdaprópriacidade:

Page 224: Data Science do zero: Primeiras regras com o Python

#tentaváriosvaloresdiferentesparakforkin[1,3,5,7]:

num_correct=0

forcityincities:location,actual_language=cityother_cities=[other_city

forother_cityincitiesifother_city!=city]

predicted_language=knn_classify(k,other_cities,location)

ifpredicted_language==actual_language:num_correct+=1

printk,"neighbor[s]:",num_correct,"correctoutof",len(cities)

Parece que três vizinhos mais próximos desempenham melhor, mostrando oresultadocorretoem59%dasvezes:

1vizinho[s]:40certosde753vizinho[s]:44certosde755vizinho[s]:41certosde757vizinho[s]:35certosde75

Agora podemos ver quais regiões seriam classificadas para quais linguagensdentro do esquema dos vizinhos mais próximos. Podemos fazer isso aoclassificarumaredeinteiracheiadepontos,eentãodemarcá-lascomofizemoscomascidades:

plots={"Java":([],[]),"Python":([],[]),"R":([],[])}

k=1#or3,or5,or...

forlongitudeinrange(-130,-60):forlatitudeinrange(20,55):

predicted_language=knn_classify(k,cities,[longitude,latitude])plots[predicted_language][0].append(longitude)plots[predicted_language][1].append(latitude)

Por exemplo, a Figura 12-2mostra o que acontece quando olhamos apenas ovizinhomaispróximo(k=1).

Vemosmuitasmudançasabruptasdeumalinguagemparaoutracomlimitesbemacentuados. Conforme aumentamos o número de vizinhos para três, vemosregiõesmaisflexíveisparacadalinguagem(Figura12-3).

Econformeaumentamososvizinhosparacinco,oslimitesficamcadavezmais

Page 225: Data Science do zero: Primeiras regras com o Python

acentuados(Figura12-4).

Aqui,nossasdimensõessãobastantecomparáveis,masseelasnãofossemvocêtalvez quisesse redimensionar os dados como fizemos em “Redimensionando”napágina132.

Figura12-2.Linguagensdeprogramação1-vizinhomaispróximo

Page 226: Data Science do zero: Primeiras regras com o Python

AMaldiçãodaDimensionalidadeOsk-vizinhosmaispróximosentramemperigoemdimensõesmaisaltasgraçasà“maldiçãodadimensionalidade”,queseresumeaofatodequeespaçosdealtadimensãosãovastos.Ospontosemespaçosdealtadimensãotendemanãoserpróximosunsdosoutros.Umamaneiradeobservarissoégerarparesdepontosaleatóriosna“unidadecubo”d-dimensionalemumavariedadededimensõesecalcularadistânciaentreeles.

Figura12-3.Linguagensdeprogramação3-vizinhosmaispróximos

Gerarpontosaleatóriosdeveserautomáticoagora:defrandom_point(dim):return[random.random()for_inrange(dim)]

Page 227: Data Science do zero: Primeiras regras com o Python

assimcomoéescreverumafunçãoquegeraasdistâncias:defrandom_distances(dim,num_pairs):return[distance(random_point(dim),random_point(dim))

for_inrange(num_pairs)]

Figura12-4.Linguagensdeprogramação5-vizinhosmaispróximos

Paracadadimensãode1até100,computaremos10.000distânciaseasusaremosparacomputaradistânciamédiaentreospontoseadistânciamínimaentreospontosdecadadimensão(Figura12-5):

dimensions=range(1,101)

avg_distances=[]min_distances=[]

random.seed(0)fordimindimensions:distances=random_distances(dim,10000)#10.000paresaleatóriosavg_distances.append(mean(distances))#rastreiaamédiamin_distances.append(min(distances))#rastreiaomínimo

Page 228: Data Science do zero: Primeiras regras com o Python

Figura12-5.Amaldiçãodadimensionalidade

Conformeonúmerodedimensões aumenta, a distânciamédia entreospontostambémaumenta.Masoqueémaisproblemáticoéa relaçãoentreadistânciamaispróximaeadistânciamédia(Figura12-6):

min_avg_ratio=[min_dist/avg_distformin_dist,avg_distinzip(min_distances,avg_distances)]

Page 229: Data Science do zero: Primeiras regras com o Python

Figura12-6.Amaldiçãodadimensionalidadenovamente

Emconjuntosdedadosdebaixadimensão,ospontosmaispróximos tendemasermaispróximosdoqueamédia.Masosdoispontosestãopróximossomentese eles estiverem próximos em todas as dimensões e cada dimensão extra—mesmosesomenteumruído—éoutraoportunidadeparacadapontosermaisdistantedosoutros.Quandohámuitasdimensões,éprovávelqueospontosmaispróximos não sejam tão próximos quanto a média, o que significa que doispontosestarempróximosnãosignificamuitacoisa (amenosquehajabastanteestrutura em seus dados que faça com que eles se comportem como seestivessememumadimensãomuitomaisbaixa).

Umaformadiferentedepensarsobreoproblemaenvolveadispersãodeespaçosdealtadimensão.

Sevocêescolher50númerosaleatóriosentre0e1,éprovávelquevocêtenhaumaboapartedointervalounitário(Figura12-7).

Page 230: Data Science do zero: Primeiras regras com o Python

Figura12-7.Cinquentapontosaleatóriosemumadimensão

Se você escolher 50 pontos aleatórios no quadrado unitário, você terá menoscobertura(Figura12-8).

Page 231: Data Science do zero: Primeiras regras com o Python

Figura12-8.Cinquentapontosaleatóriosemduasdimensões

Eemtrêsdimensõesmenosainda(Figura12-9).

matplotlibnãopermitegráficosdequatrodimensõesmuitobem,portantoesteéomáximoque iremos,masvocê jápodeverqueestãocomeçandoa tergrandesespaçosvaziossempontospertodeles.Emmaisdimensões—amenosquevocêtenhamuitomaisdados—essesespaçosgrandesevaziosrepresentamregiõesdistantesdetodosospontosquevocêquerusarnassuasprevisões.

Então, se você estiver tentando usar os vizinhos mais próximos em umadimensão mais alta, é provavelmente uma boa ideia fazer uma redução dedimensionalidadeprimeiro.

Page 232: Data Science do zero: Primeiras regras com o Python

Figura12-9.Cinquentapontosaleatóriosemtrêsdimensões

Page 233: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosscikit-learn possui muitos modelos de vizinhos mais próximos(http://bit.ly/1ycP5rj).

Page 234: Data Science do zero: Primeiras regras com o Python

CAPÍTULO13

NaiveBayes

“Euprefirooerrodoentusiasmoàindiferençadobomsenso.”—AnatoleFrance

Uma rede social não é tão boa se as pessoas não conseguem se conectar.Portanto, a DataSciencester possui um atributo popular que permite quemembrosenviemmensagensunsaosoutros.Eenquantoamaioriadosmembrossão cidadãos responsáveis que somente enviam mensagens de “como vocêestá?”,algunssãocanalhaseenviammensagensdespamsobreesquemasparaficaremricos,medicamentossemreceitaeprogramasdecredenciamentodedatascience.SeususuárioscomeçaramareclamareaVice-presidentedeMensagempediuquevocêusassedatascienceparadescobrircomofiltraressasmensagensdespam.

Page 235: Data Science do zero: Primeiras regras com o Python

UmFiltrodeSpamMuitoEstúpidoImagine um “universo” que consiste em receber umamensagem escolhida aoacasoentretodasaspossíveis.DeixeSseroevento“amensageméspam”eVseroevento“amensagemcontémapalavraviagra”.Logo,oTeoremadeBayesnos diz que a probabilidade de a mensagem ser spam depende de conter apalavraviagra:

O numerador é a probabilidade de a mensagem ser spam e conter viagra,enquantoodenominadoréapenasaprobabilidadedeamensagemconterviagra.Logo, você pode pensar nesse cálculo como uma simples representação daproporçãodemensagensviagraquesãospam.

Senóstemosumagrandecoleçãodemensagensquesabemosquesãospam,eumagrandecoleçãoquenãoéspam,nóspodemosfacilmentecalcularP(V|S)eP(V|¬S). Se presu-mirmos que qualquermensagem é igualmente provável deserspamounão-spam(assimP(S)=P(¬S)=0.5),então:

Por exemplo, se 50% das mensagens spam possuem a palavra viagra, masapenas 1% das mensagens não-spam possuem, então a probabilidade de quequalquere-mailquecontenhaviagrasejaspamé:

0.5/(0.5+0.01)=98%

Page 236: Data Science do zero: Primeiras regras com o Python

UmFiltrodeSpamMaisSofisticadoAgora imagineque temosumvocabuláriodemuitaspalavrasw1,…,wn. Parachegar neste reino da teoria da probabilidade, escreveremosXi para o evento“umamensagemcontémapalavraw1”. Imagine tambémque(pormeiodeumprocesso não-especificado-nesse-ponto) encontramos um P(Xi|S) comoestimativa para a probabilidade de a mensagem de spam conter a palavra i-ésimo, e como estimativa parecida P(Xi|¬S) para a probabilidade de umamensagemnão-spamconterapalavrai-ésimo.

AchaveparaNaiveBayeséfazera(grande)suposiçãodequeaspresenças(ouausências) de cada palavra são independentes umas das outras, condição paraumamensagem ser spam ou não. Intuitivamente, essa suposição significa quesaber seumacertamensagemde spamcontémapalavra“viagra”ounão,nãolhe dá nenhuma informação sobre amesmamensagem conter ou não palavra“rolex”.Emtermosmatemáticos,issosignificaque:

Essaéumahipóteseextrema.(Háummotivoparaconter“naive”(inocente)nonome da técnica.) Imagine que todo o nosso vocabulário consista apenas daspalavras“viagra”e“rolex”,equemetadedetodasasmensagensdespamsejam“viagra barato” e que a outra metade seja “rolex autêntico”. Nesse caso, aestimativa Naive Bayes de que uma mensagem de spam contenha ambos,“viagra”e“rolex”é:

uma vez que afastamos a teoria de que “viagra” e “rolex” nunca acontecemjuntos. Apesar da irrealidade dessa suposição, tal modelo geralmente é bem-sucedidoeéusadoemfiltrosdespamreais.

OmesmoTeoremadeBayesusadoparanosso filtrode spam“apenas_viagra”nos diz que podemos calcular a probabilidade de uma mensagem ser spamusandoaequação:

Page 237: Data Science do zero: Primeiras regras com o Python

AsuposiçãoNaiveBayespermitequecomputemoscadaumadasprobabilidadesà direita simplesmente multiplicando junto as estimativas de probabilidadeindividualparacadapalavradovocabulário.

Naprática,vocêgeralmentequerevitaramultiplicaçãodemuitasprobabilidadesao mesmo tempo, para evitar um problema chamado underflow, no qualcomputadoresnãolidambemcomnúmerosdepontosflutuantesmuitopróximosazero.Relembrandodaálgebraemquelog(ab)=loga+logbequeexp(logx)= x, nós geralmente computamos p1 *…* pn como o equivalente (mas maisamigávelaopontoflutuante):

O único desafio restante vem com as estimativas paraP(Xi|S) eP(Xi|¬S), asprobabilidadesdeumamensagemdespam(ounão-spam)conterapalavrawi.Setemosumnúmerojustodemensagensrotuladascomospamounão,aprimeiratentativaóbviaécalcularP(Xi|S)simplesmentecomoumafraçãodemensagensspamcontendoapalavrawi.

Noentanto,issocausaumgrandeproblema.Imaginequeemnossovocabuláriodetreinamentoapalavra“dado”ocorraapenasemmensagensnão-spam.Entãonós calcularíamosP(“dado”|S)=0.O resultado é que nosso classificadorNaiveBayes sempre atribuiria a probabilidade de spam 0 a qualquer mensagemcontendo a palavra “dado”, mesmo uma mensagem como “dado em viagrabarato e relógios rolex autênticos”. Para evitar esse problema, nós usaríamosalgumtipodesuavizador.

Particularmente, escolheríamos uma pseudocount—k—e calcularíamos aprobabilidadedeverapalavrai-ésimoemumspamcomo:

P(Xi|S)=(k+númerodespamscontendowi)/(2k+númerodespams)

Igualmente para P(Xi|¬S). Isso é, quando computamos as probabilidades despam para a palavra i-ésimo, nós presumimos que também vimos spamsadicionaiskcontendoapalavraekspamsadicionaisnãocontendo.

Por exemplo, se “dado” ocorre em 0/98 documentos spam e se k é 1, nóscalculamos P(“dado”|S) como 1/100 = 0,001, o que permite que nosso

Page 238: Data Science do zero: Primeiras regras com o Python

classificador atribua alguma probabilidade spam diferente de zero paramensagensquecontenhamapalavra“dado”.

Page 239: Data Science do zero: Primeiras regras com o Python

ImplementaçãoAgoratemostodosospedaçosdosquaisprecisamosparaonossoclassificador.Primeiro,vamoscriarumafunçãosimplesparaquebrar(outokenize)mensagensempalavrasdistintas.Primeiroconverteremoscadamensagemparacaixabaixa;usere.findall()paraextrair“palavras”consistentesdeletras,númeroseapóstrofo;efinalmente,useset()parapegarapenaspalavrasdistintas:

deftokenize(message):message=message.lower()#converteparaminúsculasall_words=re.findall("[a-z0-9']+",message)#extraiaspalavrasreturnset(all_words)#removeduplicadas

Nossa segunda função contará as palavras em um conjunto de mensagensrotuladas para treino. Será retornado um dicionário no qual as chaves sãopalavras, e cujos valores são listas de dois elementos [spam_count, non_spam_count],correspondentes a quantidade de vezes que vimos aquela palavra em ambasmensagens,spamenão-spam:

defcount_words(training_set):"""oconjuntoemtreinamentoconsistedepares(message,is_spam)"""counts=defaultdict(lambda:[0,0])formessage,is_spamintraining_set:

forwordintokenize(message):counts[word][0ifis_spamelse1]+=1

returncounts

Nosso próximo passo é transformar tais contas em probabilidades estimadasusandoosuavizadordescritoanteriormente.Nossafunçãoretornaráumalistadetriplas contendo cada palavra, a probabilidade de ver tal palavra em umamensagemdespameaprobabilidadedevê-laemumanão-spam:

defword_probabilities(counts,total_spams,total_non_spams,k=0.5):"""transformaoword_countsemumalistadetriplasw,p(w|spam)ep(w|~spam)"""return[(w,

(spam+k)/(total_spams+2*k),(non_spam+k)/(total_non_spams+2*k))forw,(spam,non_spam)incounts.iteritems()]

Aúltimaparteéusaressasprobabilidadesdepalavras(enossashipótesesNaiveBayes)paraatribuirprobabilidadesamensagens:

Page 240: Data Science do zero: Primeiras regras com o Python

defspam_probability(word_probs,message):message_words=tokenize(message)log_prob_if_spam=log_prob_if_not_spam=0.0

#iteracadapalavraemnossovocabulárioforword,prob_if_spam,prob_if_not_spaminword_probs:

#se“word”aparecernamensagem,#adicioneaprobabilidadelogdevê-laifwordinmessage_words:log_prob_if_spam+=math.log(prob_if_spam)log_prob_if_not_spam+=math.log(prob_if_not_spam)

#se“word”nãoaparecernamensagem#adicioneaprobabilidadelogdenãovê-la#queélog(1–probabilidadedevê-la)else:log_prob_if_spam+=math.log(1.0-prob_if_spam)log_prob_if_not_spam+=math.log(1.0-prob_if_not_spam)

prob_if_spam=math.exp(log_prob_if_spam)prob_if_not_spam=math.exp(log_prob_if_not_spam)returnprob_if_spam/(prob_if_spam+prob_if_not_spam)

PodemoscolocartudoissojuntononossoclassificadorNaiveBayes:classNaiveBayesClassifier:

def__init__(self,k=0.5):self.k=kself.word_probs=[]

deftrain(self,training_set):

#contamensagensspamenão-spamnum_spams=len([is_spam

formessage,is_spamintraining_setifis_spam])

num_non_spams=len(training_set)-num_spams

#rodadadosdetreinamentopelanossa“pipeline”word_counts=count_words(training_set)self.word_probs=word_probabilities(word_counts,

num_spams,num_non_spams,self.k)

defclassify(self,message):returnspam_probability(self.word_probs,message)

Page 241: Data Science do zero: Primeiras regras com o Python

TestandoNossoModeloUmbom(edecerta formavelho)conjuntodedadoséoSpamAssassinpubliccorpus (https://spamassassin.apache.org/publiccorpus/). Nós veremos osarquivos prefixados com 20021010. (No Windows, você precisará de umprograma como 7-Zip (http://www.7-zip.org/) para descompactar e extrair osarquivos.)

Apósextrairosdados(para,digamos,C:\spam),vocêdevetertrêspastas:spam,easy_hamehard_ham.Cadapastacontémmuitose-mails,cadaqualcontidoemumúnicoarquivo.Paramantertudobemsimples,olharemosapenasoassuntodecadae-mail.

Comoidentificamosalinhadeassunto?Olhandopelosarquivos,todosparecemcomeçarcom“Subject:”.Logo,procuraremosporisto:

importglob,re

#modifiquecomocaminhonoqualvocêcolocouosarquivospath=r"C:\spam\*\*"

data=[]

#glob.globretornatodonomedearquivoquecombinecomocaminhodeterminadoforfninglob.glob(path):

is_spam="ham"notinfn

withopen(fn,'r')asfile:forlineinfile:ifline.startswith("Subject:"):#removeoprimeiro“Subject:”emantémoquesobrousubject=re.sub(r"^Subject:","",line).strip()data.append((subject,is_spam))

Agorapodemosdividirosdadosemdadosde treinamentosedadosde testee,então,estaremosprontosparaconstruirumclassificador:

random.seed(0)#sóparaquerecebaamesmarespostaqueeutrain_data,test_data=split_data(data,0.75)

classifier=NaiveBayesClassifier()classifier.train(train_data)

Eagorapodemosverificarcomoonossomodelofaz:#triplas(subject,is_spamreal,probabilidadedespamprevisto)

Page 242: Data Science do zero: Primeiras regras com o Python

classified=[(subject,is_spam,classifier.classify(subject))forsubject,is_spamintest_data]

#presumaquespam_probability>0.5correspondeàprevisãodespam#econtaascombinaçõesde(is_spamreal,is_spamprevisto)counts=Counter((is_spam,spam_probability>0.5)

for_,is_spam,spam_probabilityinclassified)

Issodá101positivosverdadeiros(spamclassificadocomo“spam”),33positivosfalsos (ham classificados como “spam”), 704 negativos verdadeiros (hamclassificados como “ham”) e 38 negativos falsos (spam classificados como“ham”). Isso significa que nossa acurácia é 101 / (101 + 33) = 75%, e nossasensibilidade é 101 / (101 + 38) = 73%, que não são números ruins para ummodelotãosimples.

Tambéméinteressanteolharparaosmaismalclassificados:#ordenaspam_probabilitydomenorparaomaiorclassified.sort(key=lambdarow:row[2])

#asmaioresprobabilidadesdespamprevistosentreosnão-spamsspammiest_hams=filter(lambdarow:notrow[1],classified)[-5:]

#asmenoresprobabilidadesdespamprevistosentreosspamshammiest_spams=filter(lambdarow:row[1],classified)[:5]

Asduashamscommaisjeitodespampossuemaspalavras“precisa”(77vezesmais provável de aparecer em spam), “seguro” (30 vezes mais provável deaparecer em spam) e “importante” (10 vezes mais provável de aparecer emspam).

Ospamcommaisjeitodehamémuitocurto(“Re:garotas”)parajulgarmoseosegundoéuma solicitaçãode cartãode crédito emqueamaioriadaspalavrasnãoestavanoconjuntodetreinamento.

Podemosveraspalavrasquepossuemmaisjeitodespam:defp_spam_given_word(word_prob):

"""usaoteoremadebayesparacomputarp(spam|messagecontainsword)"""

#word_probéumadastriplasproduzidasporword_probabilitiesword,prob_if_spam,prob_if_not_spam=word_probreturnprob_if_spam/(prob_if_spam+prob_if_not_spam)

words=sorted(classifier.word_probs,key=p_spam_given_word)

spammiest_words=words[-5:]hammiest_words=words[:5]

Page 243: Data Science do zero: Primeiras regras com o Python

As palavras “money”, “systemworks”, “rates”, “sale” e “year” são as quepossuem mais spams, todas parecem relacionadas a tentar fazer as pessoascompraremcoisas.Easpalavras“spamBayes”,“users”,“razor”,“zzzzteana”e“sadev”sãodotipoham,emqueamaioriaparecerelacionadacomprevençãodespam,pormaisestranhoqueseja.

Como poderíamos obter uma performance melhor? Uma maneira óbvia seriapegarmaisdadospara treinar.Existemváriasmaneirasdemelhoraromodelo.Estassãoalgumaspossibilidadesquevocêpodetentar:

Olheoconteúdodamensagem,nãoolhesomentealinhadoassunto.Vocêdevesercautelosoaoverostítulosdasmensagens.Nossoclassificadorlevaemconsideraçãocadapalavraqueaparecenoconjuntodetreinamento,atémesmoaspalavrasquesóaparecemumavez.Modifiqueoclassificadorparaaceitarumlimiteopcionalmin_counteignoreossímbolosquenãoaparecemtantasvezes.Otokenizernãotempercepçãodepalavrassimilares(porexemplo,“cheap”e“cheapest”).Modifiqueoclassificadorparaterumafunçãostemmerqueconvertepalavrasparaasclassesequivalentesdepalavras.Porexemplo,umafunçãostemmersimplespodeser:

defdrop_final_s(word):returnre.sub("s$","",word)

Criarumaboafunçãostemmerédifícil.AspessoasgeralmenteusamaPorterStemmer(http://tartarus.org/martin/PorterStemmer/).Mesmoquetodasasnossascaracterísticassejam“mensagenscontendoapalavrawi´”,nãohámotivoparatal.Emnossaimplementação,nóspudemosacrescentarcaracterísticasextrascomo“mensagemcontendoumnúmero”criandotokensfictícioscomocontains:numberemodificandootokenizerparaemiti-losquandonecessário.

Page 244: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosOsartigosdePaulGraham,“APlanforSpam”(http://bit.ly/1ycPcmA)e“BetterBayesianFiltering”(http://bit.ly/1ycPbiy)(sãointeressantese)dãoumamaiorcompreensãosobreaconstruçãodefiltrosdespam.scikit-learn(http://bit.ly/1ycP9ar)contémummodeloBernoulliNBqueimplementaomesmoalgoritmoNaiveBayesqueimplementamosaqui,bemcomooutrasvariaçõesdomodelo.

Page 245: Data Science do zero: Primeiras regras com o Python

CAPÍTULO14

RegressãoLinearSimples

Aarte,comoamoralidade,consisteemestabelecerumlimiteemalgumlugar.—G.K.Cherterton

No Capítulo 5, nós usamos a função correlation para medir a força dorelacionamentolinearentreduasvariáveis.Paraamaioriadasaplicações,saberque tal relacionamento linearexistenãoéobastante.Nósqueremosconseguirentender a natureza do relacionamento. É aí que usamos regressão linearsimples.

Page 246: Data Science do zero: Primeiras regras com o Python

OModeloLembre-sedequenósestávamosinvestigandoorelacionamentoentreonúmerodeamigosdeumusuáriodaDataSciencestereotempoqueelepassanositepordia.Vamossuporquevocêseconvenceudequetermaisamigosfazaspessoaspassarem mais tempo no site, mas não das explicações alternativas quediscutimos.

A vice-presidente deRelacionamentos (Engagement) pede para você construirummodelodescrevendoessarelação.Jáquevocêencontrouumrelacionamentolinearforte,umbomlugarparacomeçaréomodelolinear.

Emparticular,vocêcriaumahipótesedequeháconstantesα(alfa)eβ(beta)taisque:

emqueyiéonúmerodeminutosqueousuárioipassanositediariamente,xiéonúmerodeamigosqueousuárioipossuieεiéumtermodeerro(esperamosquepequeno) representando o fato de existirem outros fatores não contabilizadosparaessesimplesmodelo.

Supondo que determinamos tais alpha e beta, podemos fazer previsõessimplesmentecom:

defpredict(alpha,beta,x_i):returnbeta*x_i+alpha

Comoescolhemosalphaebeta?Bom,qualquerescolhadealphaebetanosdáumasaída prevista para cada entrada x_i. Como sabemos a verdadeira saída y_i,podemoscomputaroerroparacadapar:

deferror(alpha,beta,x_i,y_i):"""errodepreverbeta*x_i+alphaquandoovalorrealéy_i"""returny_i-predict(alpha,beta,x_i)

Oque realmentegostaríamosde saber é o erro total sobre todoo conjuntodedados.Masnãoqueremosapenasadicionarerros—seaprevisãoparax_1 foraltademais e aprevisãopara x_2 for baixademais, os erros podemapenas ser

Page 247: Data Science do zero: Primeiras regras com o Python

anulados.

Então,emvezdisso,nósadicionamososerrosaoquadrado:defsum_of_squared_errors(alpha,beta,x,y):

returnsum(error(alpha,beta,x_i,y_i)**2forx_i,y_iinzip(x,y))

Asoluçãomínimadosquadradoséescolheroalphaeobetaquetornarãoasomasum_of_squared_errorsamenorpossível.

Usandocálculo(ouatediosaálgebra),aminimizaçãodeerroalphaebetaédadapor:

defleast_squares_fit(x,y):"""dadososvaloresemtreinamentoparaxey,encontraosvaloresmínimosdosquadradosdealfaebeta"""beta=correlation(x,y)*standard_deviation(y)/standard_deviation(x)alpha=mean(y)-beta*mean(x)returnalpha,beta

Sempercorreramatemáticaexata,vamospensarnoporquêessapodeserumasolução razoável. A escolha de alpha simplesmente diz que quando vemos amédiadavariável independentex, fazemosumaprevisãodamédiadavariáveldependentey.

Aescolhadebetasignificaque,quandoovalordeentradaaumentaporstandard_deviation(x),aprevisãoaumentaporcorrelation(x,y)* standard_deviation(y).Quandoxeysão perfeitamente correlacionados, um aumento de um desvio padrão em xresulta em uma divergência de um padrão de y na previsão. Quando eles sãoperfeitamentenãocorrelacionados,oaumentoemxresultaemumadiminuiçãoda previsão. E quando a correlação é zero, beta é zero, o que significa quemudançasemxnãoafetarãoaprevisão.

ÉfácilaplicarissoaosdadosdosvaloresdiscrepantesdoCapítulo5:alpha,beta=least_squares_fit(num_friends_good,daily_minutes_good)

Issodávaloresdealfa=22,95ebeta=0,903.Portanto,nossomodelodizqueesperamosqueumusuáriocomnamigospasse22,95+n*0,903minutosnositepordia.Ouseja,nósprevimosqueumusuáriosemamigosnaDataSciencesteraindaassimpassariacercade23minutosnositepordia.E,paracadaamigoadicional,

Page 248: Data Science do zero: Primeiras regras com o Python

nósesperamosqueousuáriogastequaseumminutoamaisnositepordia.

NaFigura14-1, nós assinalamos a linha de previsão para entender como essemodeloéadequadoaosdadosobservados.

Figura14-1.Nossomodelolinearsimples

Claro, nós precisamos de uma forma melhor de descobrir quão bemconseguimos ajustar os dados em vez de olhar para o gráfico. Uma medidacomuméocoeficientededeterminação(ouR²)quemedeafraçãodavariaçãototalnavariáveldependentequeécapturadapelomodelo:

deftotal_sum_of_squares(y):"""asomatotaldosquadradosdasvariaçõesdey_iapartirdesuasmédias"""returnsum(v**2forvinde_mean(y))

defr_squared(alpha,beta,x,y):"""afraçãodavariaçãoemycapturadapelomodelo,queéiguala1-afraçãodavariaçãoemynãocapturadapelomodelo"""

return1.0-(sum_of_squared_errors(alpha,beta,x,y)/

Page 249: Data Science do zero: Primeiras regras com o Python

total_sum_of_squares(y))

r_squared(alpha,beta,num_friends_good,daily_minutes_good)#0.329

Agora, nós escolhemos um alpha e beta que minimizaram a soma da previsãoquadradade erros.Ummodelo linear quepoderíamos ter escolhido é “sempreprevermean(y)”(correspondendoaalpha=mean(y)ebeta=0),cujasomadoserrosaoquadradoéexatamenteigualàsomadosquadrados.IssosignificaumR²dezero,o que indica um modelo que (obviamente, nesse caso) não possui umdesempenhomelhordoquesimplesmentepredizeramédia.

Claramente,omodelodomenorquadradodeveaomenosser tãobomquandoaquele,oquesignificaqueasomadoserrosaoquadradoénomáximoasomadosquadrados,oquesignificaqueR²deveserpelomenoszero.Easomadoserros ao quadrado deve ser pelomenos 0, o que significa queR² pode ser nomáximo1.

Quanto maior o número, melhor nosso modelo se encaixa aos dados. AquicalculamosumR²de0.329,oquenosdizquenossomodeloéapenasmaisoumenosbomemajustarosdados,equeclaramenteháoutrosfatoresemjogo.

Page 250: Data Science do zero: Primeiras regras com o Python

UsandooGradienteDescendenteSe escrevermos theta = [alpha, beta], também podemos resolver isso usando ogradientedescendente:

defsquared_error(x_i,y_i,theta):alpha,beta=thetareturnerror(alpha,beta,x_i,y_i)**2

defsquared_error_gradient(x_i,y_i,theta):alpha,beta=thetareturn[-2*error(alpha,beta,x_i,y_i),#derivadadealphaparcial

-2*error(alpha,beta,x_i,y_i)*x_i]#derivadadebetaparcial

#escolheumvaloraleatórioparacomeçarrandom.seed(0)theta=[random.random(),random.random()]alpha,beta=minimize_stochastic(squared_error,

squared_error_gradient,num_friends_good,daily_minutes_good,theta,0.0001)

printalpha,beta

Usandoosmesmosdadosnósconseguimosalpha=22,93,beta=0,905,quesãomuitopróximosdasrespostascorretas.

Page 251: Data Science do zero: Primeiras regras com o Python

EstimativaMáximadaProbabilidadePorqueescolhemosmínimosquadrados?Umajustificativaenvolveaestimativamáximadaprobabilidade.

Imaginequetemosummodelodedadosv1,…,vnquevemdeumadistribuiçãoquedependedealgumparâmetrodesconhecidoθ:

Senãosoubéssemos teta,poderíamosvoltarepensarnessaquantidadecomoaprobabilidadedeθdadaaamostra:

Nessa abordagem, o θ mais provável é o valor quemaximiza essa função deprobabilidade; isto é, o valor que faz com que os dados observados sejam osmais prováveis. No caso de uma distribuição contínua, na qual temos umafunção de distribuição de probabilidade no lugar de uma função demassa deprobabilidade,nóspodemosfazeramesmacoisa.

Devoltaàregressão.Umasuposiçãoquegeralmenteéfeitasobreomodeloderegressãosimpleséqueoserrosderegressãosãonormalmentedistribuídoscommédia 0 e algum (conhecido) desvio padrão σ. Se esse for o caso, então aprobabilidadebaseadaemverumpar(x_i,y_i)é:

A probabilidade baseada em todo o conjunto de dados é o produto deprobabilidades individuais, que é maior precisamente quando alpha e beta sãoescolhidos para minimizar a soma dos erros quadrados. Isto é, nesse caso,minimizar a soma dos erros quadrados é equivalente a maximizar aprobabilidadedosdadosobservados.

Page 252: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosContinuelendosobreregressãomúltipladoCapítulo15!

Page 253: Data Science do zero: Primeiras regras com o Python

CAPÍTULO15

RegressãoMúltipla

Eunãoolhoparaumproblemaecolocovariáveisquenãooafetam.—BillParcells

Mesmo que a vice-presidente esteja impressionada com seumodelo preditivo,ela achaquevocêpode fazermelhor.Para isso,vocêcolheudadosadicionais:paracadaumdosseususuários,vocêsabequantashoraseletrabalhapordiaeseeletemumPhD.Vocêgostariadeusaressesdadosadicionaisparamelhorarseumodelo.

Desta forma,vocêcriaumahipótesedeummodelo linear commaisvariáveisindependentes:

Obviamente,seumusuáriotemPhDnãoéumnúmero,mas—comofalamosnoCapítulo 11 — nós podemos introduzir uma variável fictícia igual a 1 parausuárioscomPhDe0parausuáriossemPhD,oqueétãonuméricoquantoasoutrasvariáveis.

Page 254: Data Science do zero: Primeiras regras com o Python

OModeloLembre-sedequenoCapítulo14nósadaptamosummodelodaforma:

Agoraimaginequecadaentradaxinãoéumúniconúmeromasumvetordeknúmerosxi1,…,xik.Omodeloderegressãomúltiplapresumeque:

Emregressãomúltiplaovetordeparâmetrosgeralmenteéchamadodeβ.Nósqueremosqueissoincluaotermoconstantetambém,oquenóspodemosatingiradicionandoumacolunadeunsaosnossosdados:

beta=[alpha,beta_1,...,beta_k]

e:x_i=[1,x_i1,...,x_ik]

Entãonossomodeloé:defpredict(x_i,beta):

"""presumequeoprimeiroelementodecadax_ié1"""returndot(x_i,beta)

Nesse caso em particular, nossa variável independente x será uma lista devetores,cadaumseparecendocom:

[1,#termoconstante49,#númerodeamigos4,#horasdetrabalhopordia0]#nãotemPhD

Page 255: Data Science do zero: Primeiras regras com o Python

••

MaisSuposiçõesdoModelodosMínimosQuadradosHámais algumas suposições que são exigidas para que essemodelo (e nossasolução)façasentido.

Aprimeiraéqueascolunasdex sejam linearmente independentes—quenãohajacomoescreverqualquerumcomoumasomaponderadadosoutros.Seestasuposição falhar, é impossívelestimarbeta.Paraver issoemumcasoextremo,imagineque temosumcampo extra num_acquaintances em nossos dados que paracadausuáriofosseexatamenteigualanum_friends.

Então, começando comqualquer beta, se adicionarmosqualquer quantidade aocoeficiente num_friends e subtrair a mesma quantidade ao coeficientenum_acquaintances, as previsões do modelo permanecerão as mesmas. O quesignificaquenãohácomoencontrarocoeficienteparanum_friends.(Geralmente,violaçõesaestasuposiçãonãosãotãoóbvias.)

Asegundahipóteseimportanteéqueascolunasdexnãoestãocorrelacionadascom os erros ε. Se isto calhar de acontecer, nossas estimativas de beta estarãosistematicamenteerradas.

Porexemplo,noCapítulo14,nósconstruímosummodelopreditivoemquecadaamigoadicionalestavaassociadocom0,90minutosextrasnosite.

Imaginequetambéméocasoque:Pessoasquetrabalhammaishoraspassammenostemponosite.Pessoascommaisamigostendematrabalharmaishoras.

Istoé,imaginequeomodelorealé:

minutos=α+β1amigos+β2horasdetrabalho+ε

equehorasdetrabalhoeamigossãopositivamentecorrelacionados.Nestecaso,quandominimizarmososerrosdeummodelovariável:

minutos=α+β1amigos+ε

nóssubestimaremosβ1.

Page 256: Data Science do zero: Primeiras regras com o Python

Pensenoqueaconteceriasenósfizéssemosprevisõesusandoummodelodeumaúnicavariávelcomovalor“real”deβ1.(Istoé,ovalorqueapareceapartirdaminimização de erros é o que chamamos de modelo “real”.) As previsõestenderiam a ser muito pequenas para usuários que trabalham muitas horas emuito grandes para os que trabalham poucas horas, pois β2 > 0 e nós“esquecemos” de inclui-lo. Porque horas de trabalho é positivamentecorrelacionadacomonúmerodeamigos,istosignificaqueasprevisõestendema sermuito pequenas para usuários commuitos amigos emuito grandes parausuárioscompoucosamigos.

Oresultadodissoéquepodemosreduziroserros(nomodelodeúnicavariável)diminuindonossaestimativadeβ1,quesignificaqueoβ1queminimizaoerroémenordoqueovalor“real”.E, emgeral,quandovariáveis independentes sãocorrelacionadascomerroscomoaqui,nossasoluçãodosmínimosquadradosnosdaráumaestimativapolarizadadeβ.

Page 257: Data Science do zero: Primeiras regras com o Python

AjustandooModeloComo fizemos no modelo linear simples, escolheremos beta para minimizar asomadoserrosquadrados.Encontrarasoluçãoexatanãoétãosimplesdefazeramão, o que significa que precisamos usar o gradiente descendente.Começaremos criando uma função de erro a minimizar. Para o gradientedescendentealeatório,queremosapenasoerroquadradocorrespondenteaumasimplesprevisão:

deferror(x_i,y_i,beta):returny_i-predict(x_i,beta)

defsquared_error(x_i,y_i,beta):returnerror(x_i,y_i,beta)**2

Sevocêsabecálculo,vocêpodecomputar:defsquared_error_gradient(x_i,y_i,beta):

"""ogradiente(comrespeitoabeta)correspondenteaoi-ésimotermodeerroquadrado"""return[-2*x_ij*error(x_i,y_i,beta)

forx_ijinx_i]

Casocontrário,vocêprecisaráacreditarnaminhapalavra.

Nestemomento,estamosprontosparaencontrarobetaótimousandoogradientedescendentealeatório:

defestimate_beta(x,y):beta_initial=[random.random()forx_iinx[0]]returnminimize_stochastic(squared_error,

squared_error_gradient,x,y,beta_initial,

0.001)

random.seed(0)beta=estimate_beta(x,daily_minutes_good)#[30.63,0.972,-1.868,0.911]

Issosignificaquenossomodeloseparececomisso:

minutos=30,63+0,972amigos–1,868horasdetrabalho+0,911PhD

Page 258: Data Science do zero: Primeiras regras com o Python

InterpretandooModeloVocê deveria pensar nos coeficientes dos modelos como representantes deestimativas dos impactos de cada fator com-todos-os-demais-mantidos-constantes. O restante sendo igual, cada amigo adicional corresponde a umminuto extra passado no site a cada dia. O restante sendo igual, cada horaadicional no trabalho de um usuário corresponde a aproximadamente doisminutos menos gastos no site por dia. O restante sendo igual, ter um PhD éassociadoapassarumminutoextranositeacadadia.

Issonãonosdiz(diretamente)nadaarespeitodasinteraçõesentreasvariáveis.Épossívelqueoefeitodehorasdetrabalhosejadiferenteparapessoascommuitosamigosdoqueparapessoascompoucosamigos.Estemodelonãocapturaisso.Umaformadelidarcomtalcasoéinserirumanovavariávelquesejaoprodutode“amigos”e“horasdetrabalho".Issoefetivamentepermitequeocoeficientede “horas de trabalho” aumente (ou diminua) conforme o número de amigosaumenta.

Ouépossívelquequantomaisamigosvocêtemmaistempovocêpassenositeaté certo ponto, e após isso mais amigos fazem com que você passe menostempo no site. (Talvez com muitos amigos a experiência seja demais?) Nóspoderíamostentarcapturarissoemnossomodeloadicionandooutravariávelquesejaoquadradodonúmerodeamigos.

Umavezquecomeçamosaadicionarvariáveis,precisamosnospreocuparseoscoeficientes são “importantes”. Não há limites para os números de produtos,logs,quadradosepotênciasdeLouisGracequepodemosadicionar.

Page 259: Data Science do zero: Primeiras regras com o Python

OBenefíciodoAjusteMaisumavez,nóspodemosolharparaR²,queagoraaumentoupara0,68:

defmultiple_r_squared(x,y,beta):sum_of_squared_errors=sum(error(x_i,y_i,beta)**2

forx_i,y_iinzip(x,y))return1.0-sum_of_squared_errors/total_sum_of_squares(y)

Lembre-se, entretanto, que adicionar novas variáveis a uma regressão iránecessariamenteaumentarR².Afinal,omodeloderegressãosimpleséapenasocasoespecialdomodeloderegressãomúltiplaemqueoscoeficientesde“horasdetrabalho”e“PhD”sãoiguaisa0.Omelhormodeloderegressãomúltiplateránecessariamenteumerropelomenostãopequenoquantoaquele.

Por isso, em regressãomúltipla, nós tambémprecisamosver oserros padrõesdos coeficientes, quemedem quão certos estamos em nossas estimativas paracadaβ1.Aregressãocomoumtodopodeajustarnossosdadosmuitobem,massealgumasdasvariáveisindependentesforemcorrelacionadas(ouirrelevantes),seuscoeficientespodemnãosignificartanto.

Aabordagemtípicaparamediresteserroscomeçacomoutrasuposição—queerros εi são variáveis independentes aleatórias normais commédia 0 e algum(desconhecido) desvio padrão ơ compartilhado. Neste caso, nós (ou, maisprovavelmente,nossosoftwaredeestatística)podemosusaraálgebralinearparaencontraroerropadrãoparacadacoeficiente.Quantomaiorfor,menoscertezatemnossomodelosobreocoeficiente.Infelizmente,nãopodemosfazeressetipodeálgebralineardozero.

Page 260: Data Science do zero: Primeiras regras com o Python

Digressão:AInicializaçãoImagine que temos um modelo de n pontos de dados gerados por algumadistribuição(desconhecidapornós:

data=get_sample(num_points=n)

No Capítulo 5, escrevemos uma função median para computar a mediana dosdadosobservados,aqualpodemosusarcomoestimativadaprópriamedianadadistribuição.

Masquãoconfiantespodemosestarcomrelaçãoànossaestimativa?Setodososdadosnoexemplosãopróximosa100,parecequeamedianaépróximaa100.Se aproximadamente metade dos dados no modelo forem próximos de 0 e aoutra metade próxima de 200, então não podemos estar tão certos sobre amediana.

Se pudéssemos pegar novos modelos repetidamente, poderíamos computar amedianaparacadaeveradistribuiçãodelas.Geralmentenãopodemos.Oquepodemos fazer é inicializar novos conjuntos de dados escolhendon pontos dedadoscomsubstituiçãoapartirdenossosdadoseentãocomputarasmedianasdestesconjuntosdedadossintéticos:

defbootstrap_sample(data):"""amostraaleatoriamentelen(dados)elementoscomsubstituição"""return[random.choice(data)for_indata]

defbootstrap_statistic(data,stats_fn,num_samples):"""avaliastats_fnemnum_samplesamostradeinicializaçãoapartirdosdados"""return[stats_fn(bootstrap_sample(data))

for_inrange(num_samples)]

Porexemplo,considereosdoisseguintesconjuntosdedados:#101pontostodosmuitopróximosde100close_to_100=[99.5+random.random()for_inrange(101)]

#101pontos,50próximosde0,50próximosde200far_from_100=([99.5+random.random()]+

[random.random()for_inrange(50)]+[200+random.random()for_inrange(50)])

Sevocêcomputar amedianapara cada, ambas serãomuitospróximasde100.Entretanto,sevocêolharpara:

Page 261: Data Science do zero: Primeiras regras com o Python

bootstrap_statistic(close_to_100,median,100)

você verá em sua maioria números próximos de 100. Enquanto que, se vocêolharpara:

bootstrap_statistic(far_from_100,median,100)

vocêverámuitosnúmerospróximosde0emuitospróximosde200.

Odesviopadrãodoprimeiroconjuntodemedianasépróximode0enquantoqueodosegundoépróximode100.Estecasoextremoseriamutofácildeperceberinspecionandomanualmenteosdadosmas,nogeral,issonãoéverdade.

Page 262: Data Science do zero: Primeiras regras com o Python

ErrosPadrõesdeCoeficientesdeRegressãoNós podemos usar a mesma abordagem para calcular os erros padrões dosnossos coeficientes de regressão. Nós repetidamente tiramos uma amostra deinicializaçãodosnossosdadosecalculamosbetabaseadonaquelaamostra.Seocoeficiente correspondente a uma das variáveis independentes (digamosnum_friends) nãovariarmuitos pelosmodelos, nóspodemos ficar confiantes quenossa estimativa está relativamente correta. Se o coeficiente variarmuito, nãopodemosficartãoconfiantesassim.

Oúnicodetalheéque,antesdaamostragem,precisamoscompactar(zip)nossosdados x e y para certificar-nos de que valores correspondentes de variáveisindependentes e dependentes sejam amostrados juntos. Isso significa quebootstrap_sampleretornaráumalistadepares(x_i,y_i),queprecisarásereagruparemx_sampleey_sample.

defestimate_sample_beta(sample):"""amostraéumalistadepares(x_i,y_i)"""x_sample,y_sample=zip(*sample)#truquemágicoparadescompactarreturnestimate_beta(x_sample,y_sample)

random.seed(0)#paraquevocêconsigaomesmoresultadoqueeu

bootstrap_betas=bootstrap_statistic(zip(x,daily_minutes_good),estimate_sample_beta,100)

Eapósissopodemoscalcularodesviopadrãodecadacoeficiente:bootstrap_standard_errors=[

standard_deviation([beta[i]forbetainbootstrap_betas])foriinrange(4)]

#[1,174,#termoconstante,erroreal=1,19#0,079,#num_friends,erroreal=0,080#0,131,#desempregado,erroreal=0,127#0,990]#phd,erroreal=0,998

Nóspodemosusarissoparatestarhipóteses,como“βiéigualazero?”deacordocomahipótesenulaβ1=0(ecomnossasoutraspremissassobreadistribuiçãodeεi),aestatística:

Page 263: Data Science do zero: Primeiras regras com o Python

que é nossa estimativa de β1, dividida pela nossa estimativa de erro padrão,segueumadistribuiçãotdestudentcom“n–kpontosdeliberdade".

Se tivéssemos a função students_t_cdf poderíamos computar valores p para cadacoeficiente mínimo quadrado para indicar a probabilidade de observarmos talvalor se o coeficiente real fosse zero. Infelizmente, nós não temos tal função.(Masteríamossenãoestivéssemostrabalhandoapartirdozero.)

Entretanto,conformeosgrausdeliberdadeaumentam,adistribuiçãotficamaisperto de um padrão normal. Em uma situação como essa, em que n é muitomaiorquek,nóspodemosusarnormal_cdfeaindanossentirbem:

defp_value(beta_hat_j,sigma_hat_j):ifbeta_hat_j>0:

#seocoeficienteépositivo,precisamoscomputarduasvezesa#probabilidadedeverumvalorainda*maior*return2*(1-normal_cdf(beta_hat_j/sigma_hat_j))

else:#casocontrário,duasvezesaprobabilidadedeverumvalor*menor*return2*normal_cdf(beta_hat_j/sigma_hat_j)

p_value(30.63,1.174)#~0(termoconstante)p_value(0.972,0.079)#~0(num_friends)p_value(-1.868,0.131)#~0(work_hours)p_value(0.911,0.990)#0.36(phd)

(Em uma situação não como essa, nós estaríamos usando um programaestatístico que sabe tanto computar a distribuição t quanto os erros padrõesexatos.)

Enquantoamaioriadoscoeficientespossuemvaloresppequenos(sugerindoqueeles realmente não são zeros), o coeficiente para “PhD” não é“significantemente”diferentedezero,oquetornapossívelqueocoeficientede“PhD”sejaaleatório.

Emcenáriosde regressãomaiselaborados,àsvezesvocêquer testarhipótesesmaiselaboradassobreosdados,como“pelomenosumdeβ1nãoézero”ou“β1éigualaβ2eβ3éigualaβ4”,quevocêpodefazercomumtesteF,queestáforadoescopodestelivro.

Page 264: Data Science do zero: Primeiras regras com o Python

RegularizaçãoNa prática, você geralmente aplicará regressão linear em conjuntos de dadoscomgrandesquantidadesdevariáveis.Issocriaalgumasrugasextras.Primeiro,quantomaisvariáveisvocêusar,maioraprobabilidadedevocêsobreajustarseumodelo ao conjuntode treinamento.E segundo,quantomais coeficientesnão-zero você tiver, mais difícil será entendê-los. Se o objetivo é explicar algumfenômeno,ummodelopequenocom três fatorespodesermaisútildoqueummodeloumpoucomelhorcomcentenas.

Aregularização é uma abordagem na qual nós adicionamos ao termo de erroumapenalidadeque aumenta beta aumenta.Então nósminimizamos o erro e apenalidade combinadas. Quanto mais importância dermos em termos depenalidade,maisdesencorajamoscoeficientesgrandes.

Por exemplo, em regressãode cumeeira (ridge), adicionamos uma penalidadeproporcional à soma dos quadrados de beta_i. (Exceto que nós tipicamente nãopenalizamosbeta_o,otermoconstante.)

#alphaéum*hiperparâmetro*quecontrolaquãoseveraapenalidadeé#àsvezeséchamadode“lambda”masissojátemumsignificadoemPythondefridge_penalty(beta,alpha):

returnalpha*dot(beta[1:],beta[1:])

defsquared_error_ridge(x_i,y_i,beta,alpha):"""estimativadeerromaisapenalidaderidgesobrebeta"""returnerror(x_i,y_i,beta)**2+ridge_penalty(beta,alpha)

quevocêpodeentãocolocarogradientedescendentecomosempre:defridge_penalty_gradient(beta,alpha):

"""gradientesomentedepenalidaderidge"""return[0]+[2*alpha*beta_jforbeta_jinbeta[1:]]

defsquared_error_ridge_gradient(x_i,y_i,beta,alpha):"""gradientecorrespondenteaoi-ésimotermodeerroquadradoincluindoapenalidaderidge"""returnvector_add(squared_error_gradient(x_i,y_i,beta),

ridge_penalty_gradient(beta,alpha))

defestimate_beta_ridge(x,y,alpha):"""usaogradientedescendenteparaencaixarumaregressãoridgecompenalidadealfa"""beta_initial=[random.random()forx_iinx[0]]

Page 265: Data Science do zero: Primeiras regras com o Python

returnminimize_stochastic(partial(squared_error_ridge,alpha=alpha),partial(squared_error_ridge_gradient,alpha=alpha),x,y,beta_initial,0.001)

Comalphaemzero,nãohápenalidadeeconseguimososmesmosresultadosdeantes:

random.seed(0)beta_0=estimate_beta_ridge(x,daily_minutes_good,alpha=0.0)#[30.6,0.97,-1.87,0.91]dot(beta_0[1:],beta_0[1:])#5.26multiple_r_squared(x,daily_minutes_good,beta_0)#0.680

Conformeaumentamosalpha,obenefíciodoajustepiora,masotamanhodebetadiminui:

beta_0_01=estimate_beta_ridge(x,daily_minutes_good,alpha=0.01)#[30.6,0.97,-1.86,0.89]dot(beta_0_01[1:],beta_0_01[1:])#5.19multiple_r_squared(x,daily_minutes_good,beta_0_01)#0.680

beta_0_1=estimate_beta_ridge(x,daily_minutes_good,alpha=0.1)#[30.8,0.95,-1.84,0.54]dot(beta_0_1[1:],beta_0_1[1:])#4.60multiple_r_squared(x,daily_minutes_good,beta_0_1)#0.680

beta_1=estimate_beta_ridge(x,daily_minutes_good,alpha=1)#[30.7,0.90,-1.69,0.085]dot(beta_1[1:],beta_1[1:])#3.69multiple_r_squared(x,daily_minutes_good,beta_1)#0.676

beta_10=estimate_beta_ridge(x,daily_minutes_good,alpha=10)#[28.3,0.72,-0.91,-0.017]dot(beta_10[1:],beta_10[1:])#1.36multiple_r_squared(x,daily_minutes_good,beta_10)#0.573

Em particular, o coeficiente em “PhD” some conforme aumentamos apenalidade, o que está de acordo com nosso resultado anterior que não foisignificantementediferentedezero.

Geralmente,vocêquereriareescalarseusdadosantesdeusaressaabordagem.Afinal,se vocêmuda anos de experiência para séculos de experiência, seu coeficiente demínimoquadradoaumentarporumfatorde100einesperadamenteserápenalizadomuitomais,mesmosendoomesmomodelo.

Page 266: Data Science do zero: Primeiras regras com o Python

Outraabordageméaregressãolaço(lasso),queusaapenalidade:deflasso_penalty(beta,alpha):

returnalpha*sum(abs(beta_i)forbeta_iinbeta[1:])

Enquanto a penalidade de cumeeira diminui os coeficientes no geral, apenalidade laço tendea forçaros coeficientes a seremzero,oquea tornaboaparaaprendermodelosesparsos.Infelizmente,nãoéagradávelparaogradientedescendente,oquesignificaquenósnãoconseguiremosresolvê-ladozero.

Page 267: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosAregressãopossuiumateoriaricaeexpansiva.Esteéoutroassuntoquevocêdeveriaconsiderarlerumlivrodidáticoou,pelomenos,artigosdoWikipédia.scikit-learnpossuiummódulolinear_model(http://bit.ly/1ycPg63)queforneceummodeloLinearRegressionsimilaraonosso,bemcomoumaregressãoRidge,regressãoLassoeoutrostiposderegularização.

Statsmodel(http://statsmodels.sourceforge.net)éoutrotipodemóduloPythonquecontém(entreoutrascoisas)modelosderegressãolinear.

Page 268: Data Science do zero: Primeiras regras com o Python

CAPÍTULO16

RegressãoLogística

Muitaspessoasdizemqueháumalinhatênueentreagenialidadeealoucura.Nãoachoqueexistaumalinhatênue,euachoqueháumabismo.

—BillBailey

NoCapítulo 1, nós demos uma pequena olhada no problema de tentar preverquaisusuáriosdaDataSciencesterpagavamporcontaspremium.Revisitaremosesseproblemanestecapítulo.

Page 269: Data Science do zero: Primeiras regras com o Python

OProblemaNóstemosumconjuntodedadosanônimosdeaproximadamente200usuários,contendoosaláriodecadausuário,seusanosdeexperiênciacomocientistasdedadosesepagamporumacontapremium(Figura16-1).Comoécomumcomvariáveiscategóricas,nósrepresentamosasvariáveisdependentescomo0(semcontapremium)ou1(contapremium).

Comodecostume,nossosdadosestãoemumamatriznaqualcadafileiraéumalista [experience, salary, paid_account]. Vamos transformá-la no formato do qualprecisamos:

x=[[1]+row[:2]forrowindata]#cadaelementoé[1,experience,salary]y=[row[2]forrowindata]#cadaelementoépaid_account

Uma primeira tentativa óbvia é usar a regressão linear e encontrar o melhormodelo:

contapaga=β0+β1experiência+β2salário+˚

Page 270: Data Science do zero: Primeiras regras com o Python

Figura16-1.Usuáriospagantesenãopagantes

Ecertamentenãohánadaquenosimpeçademodelaroproblemadessaforma.OsresultadossãoexibidosnaFigura16-2.

rescaled_x=rescale(x)beta=estimate_beta(rescaled_x,y)#[0.26,0.43,-0.43]predictions=[predict(x_i,beta)forx_iinrescaled_x]

plt.scatter(predictions,y)plt.xlabel("prevista")plt.ylabel("realizada")plt.show()

Page 271: Data Science do zero: Primeiras regras com o Python

Figura16-2.Usandoregressãolinearparaprevercontaspremium

Masessaabordagemlevaaalgunsproblemasimediatos:Nósgostaríamosqueassaídasdanossaprevisãofossem0ou1,paraindicaramembresiadaclasse.Estariatudobemseelesestivessementre0e1,umavezquenóspodemosinterpretaressasprobabilidades—umasaídade0,25poderiasignificar25%dechancedeserumsóciopagante.Massaídasdomodelolinearpodemsergrandesnúmerospositivosouatémesmonúmerosnegativos,fazendocomqueainterpretaçãonãosejaclara.Defato,aquimuitasdasnossasprevisõesforamnegativas.Omodeloderegressãolinearpresumiuqueoserrosnãoeramcorrelacionadoscomascolunasdex.Masaqui,ocoeficientederegressãoparaexperienceé0,43,indicandoquemaisexperiêncialevaamaioresprobabilidadesdeumacontapremium.Issosignificaquenossomodeloapresentavaloresmuitograndesparapessoascommuitaexperiência.Mas

Page 272: Data Science do zero: Primeiras regras com o Python

nóssabemosqueosvaloresreaisdevemser,nomáximo1,oquesignificaquesaídasmuitograndescorrespondemavaloresnegativosmuitoaltosnostermosdeerros.Sendoesseocaso,nossaestimativaparabetaépolarizada.

O que nós gostaríamos é que grandes valores positivos de dot(x_i, beta)

correspondessemàsprobabilidadespróximasa1equegrandesvaloresnegativoscorrespondessem às probabilidades próximas a 0. Nós podemos realizar issoaplicandooutrafunçãonoresultado.

Page 273: Data Science do zero: Primeiras regras com o Python

AFunçãoLogísticaNocasodaregressãologística,usamosafunçãologística,exibidanaFigura16-3:

deflogistic(x):return1.0/(1+math.exp(-x))

Figura16-3.Afunçãologística

Conformesuaentradaficagrandeepositiva,elaseaproximacadavezmaisde1.Conforme sua entrada fica grande e negativa, se aproxima mais de 0. Alémdisso,elatemapropriedadeconvenientedasuaderivadaserdadapor:

deflogistic_prime(x):returnlogistic(x)*(1-logistic(x))

a qual nós utilizaremos em um instante. Nós usaremos isto para ajustar ummodelo:

Page 274: Data Science do zero: Primeiras regras com o Python

emqueféafunçãologistic.

Lembre-seque,paraaregressãolinear,nósajustamosomodelosminimizandoasoma dos erros quadrados, o que acabou escolhendo o β que maximizou aprobabilidadedosdados.

Aquiasduasnãosãoequivalentes,entãousaremosogradientedescendenteparamaximizaraprobabilidadediretamente.Issosignificaqueprecisamoscalcularafunçãodeprobabilidadeeseugradiente.

Dado algum β, nosso modelo diz que cada y1 deveria ser igual a 1 comprobabilidadef(xiβ)e0comprobabilidade1–f(xiβ).

Opdfparay1podeserescritocomo:

sey1é0,issoéiguala:

esey1é1,éiguala:

Acabasendomaissimplesmaximizarologdaprobabilidade:

Porqueafunçãologémonotonamentecrescente,qualquerbetaquemaximizeologdaprobabilidadetambémmaximizaaprobabilidadeevice-versa:

deflogistic_log_likelihood_i(x_i,y_i,beta):ify_i==1:

returnmath.log(logistic(dot(x_i,beta)))else:

returnmath.log(1-logistic(dot(x_i,beta)))

Sesupormosquepontosdedadosdiferentessãoindependentesunsdosoutros,aprobabilidade total éoprodutodasprobabilidades individuais.Oque significaqueologtotaldaprobabilidadeéasomadasprobabilidadesdoslogindividuais;

Page 275: Data Science do zero: Primeiras regras com o Python

deflogistic_log_likelihood(x,y,beta):returnsum(logistic_log_likelihood_i(x_i,y_i,beta)

forx_i,y_iinzip(x,y))

Umpoucodecálculonosforneceogradiente:deflogistic_log_partial_ij(x_i,y_i,beta,j):

"""aquiiéoíndicedopontodedados,jéoíndicedaderivada"""

return(y_i-logistic(dot(x_i,beta)))*x_i[j]

deflogistic_log_gradient_i(x_i,y_i,beta):"""ogradientedologdaprobabilidadecorrespondenteaoi-ésimopontodedados"""

return[logistic_log_partial_ij(x_i,y_i,beta,j)forj,_inenumerate(beta)]

deflogistic_log_gradient(x,y,beta):returnreduce(vector_add,

[logistic_log_gradient_i(x_i,y_i,beta)forx_i,y_iinzip(x,y)])

nestepontonóstemostodosospedaçosqueprecisamos.

Page 276: Data Science do zero: Primeiras regras com o Python

AplicandooModeloQueremosdividirnossosdadosemconjuntodetreinamentoeconjuntodeteste:

random.seed(0)x_train,x_test,y_train,y_test=train_test_split(rescaled_x,y,0.33)

#queremosmaximizarologdaprobabilidadeemdadosdetreinamentofn=partial(logistic_log_likelihood,x_train,y_train)gradient_fn=partial(logistic_log_gradient,x_train,y_train)

#escolhemosumpontodepartidaaleatóriobeta_0=[random.random()for_inrange(3)]

#emaximizamosusandoogradientedescendentebeta_hat=maximize_batch(fn,gradient_fn,beta_0)

Comoalternativa,vocêpoderiausarogradientedescendenteestocástico:beta_hat=maximize_stochastic(logistic_log_likelihood_i,

logistic_log_gradient_i,x_train,y_train,beta_0)

Dequalquerforma,encontramosaproximadamente:beta_hat=[-1.90,4.05,-3.87]

Essessãooscoeficientesparaosdadosrescaled(redimensionados),maspodemostransformá-losdevoltaaosdadosoriginais:

beta_hat_unscaled=[7.61,1.42,-0.000249]

Infelizmente, eles não são tão fáceis de interpretar como os coeficientes deregressãolinear.Orestantesendoigual,umanoamaisdeexperiênciaacrescenta1,42àentradadelogistic.Orestantesendoigual,10.000amaisnosaláriodiminui2,49daentradadelogistic.

Noentanto,oimpactonasaídadependedeoutrasentradastambém.Sedot (beta,x_i)jáégrande(correspondenteaumaprobabilidadepróximade1),aumentá-lomesmoquemuitonãopodeafetarmuitoaprobabilidade.Seforpróximade0,aumentá-loumpoucopodeaumentarbastanteaprobabilidade.

Oquepodemosdizer éque— todoo restante sendo igual—aspessoascommaisexperiênciatêmmaisprobabilidadedepagarpelaassinatura.Etambém,—todoorestantesendoigual—aspessoascomossaláriosmaisaltossãomenos

Page 277: Data Science do zero: Primeiras regras com o Python

prováveisdepagarporassinaturas.(Estavaumtantoaparentequandomontamosográficodosdados.)

Page 278: Data Science do zero: Primeiras regras com o Python

OBenefíciodoAjusteNós ainda não utilizamos os dados de teste. Vamos ver o que acontece sefizermosaprevisãodecontapagaquandoaprobabilidadeexceder0,5:

true_positives=false_positives=true_negatives=false_negatives=0

forx_i,y_iinzip(x_test,y_test):predict=logistic(dot(beta_hat,x_i))

ify_i==1andpredict>=0.5:#PV:pagaeprevimospagatrue_positives+=1

elify_i==1:#FN:pagaeprevimosnãopagantesfalse_negatives+=1

elifpredict>=0.5:#VF:nãopagaeprevimospagantesfalse_positives+=1

else:#VP:nãopagaeprevimosnãopagatrue_negatives+=1

precision=true_positives/(true_positives+false_positives)recall=true_positives/(true_positives+false_negatives)

Isso dá uma acurácia de 93% (“quando previmos conta paga estamos certos93%dotempo”)eumasensibilidadede82%(“quandoumusuáriopagouumacontaeprevimoscontapaga82%dotempo”),emqueambossãonúmerosbemrespeitáveis.

Nós tambémpodemosassinalarasprevisõesversusas reais (Figura16-4),quetambémmostraqueomodelofuncionabem:

predictions=[logistic(dot(beta_hat,x_i))forx_iinx_test]plt.scatter(predictions,y_test)plt.xlabel("probabilidadeprevista")plt.ylabel("resultadoreal")plt.title("RegressãoLogísticaPrevistavs.Real")plt.show()

Page 279: Data Science do zero: Primeiras regras com o Python

Figura16-4.Regressãologísticaprevistaversusreal.

Page 280: Data Science do zero: Primeiras regras com o Python

MáquinadeVetordeSuporteEsseconjuntodepontosemquedot(beta_hat,x_i)éiguala0éolimiteentrenossasclasses.Nóspodemosassinalá-loparaverexatamenteoquenossomodeloestáfazendo(Figura16-5).

Esselimiteéumhiperplanoquedivideoespaçodeparâmetroentreduaspartesdeespaçocorrespondentesapreverpagoseprevernãopagos.Nósencontramosissocomoumefeitocolateraldeencontraromodelologísticomaisprovável.

Umaabordagemalternativaparaaclassificaçãoéapenasprocurarohiperplanoque“melhor”separeasclassesnosdadosdetreinamento.Essaéaideiaportrásdamáquina de vetor de suporte, que encontra o hiperplano que maximiza adistânciaparaopontomaispróximoemcadaclasse(Figura16-6).

Figura16-5.Usuáriospagantesenãopagantescomlimitededecisão

Page 281: Data Science do zero: Primeiras regras com o Python

Encontrartalhiperplanoéumproblemadeotimizaçãoqueenvolvetécnicasquesãoavançadasdemaisparanós.Umproblemadiferenteéqueumhiperplanodeseparaçãopodenemmesmoexistir.Emnossoconjuntodedados“quempaga?”simplesmente não há linha que separe perfeitamente os usuários pagantes dosnãopagantes.

Nóspodemos,àsvezes,contornaressasituaçãotransformandoosdadosemumespaço dimensional superior. Por exemplo, considere o simples conjuntounidimensionaldedadosexibidonaFigura16-7.

Figura16-6.Umhiperplanodeseparação

Estáclaroquenãoháhiperplanoquesepareexemplospositivosdosnegativos.Entretanto,olheoqueacontecequandomapeamosesseconjuntodedadosemduas dimensões diferentes enviando o ponto x para (x, x**2). De repente, épossívelencontrarumhiperplanoquedivideosdados(Figura16-8).

Page 282: Data Science do zero: Primeiras regras com o Python

Isso égeralmente chamadode truquedo kernel porque, emvez demapear ospontos num espaço dimensionalmaior (o que pode ser caro se houvermuitospontoseomapeamentoforcomplicado),nósusamosumafunção“kernel”paracomputar produtos escalares no espaço dimensional maior e usá-los paraencontrarohiperplano.

Figura16-7.Umconjuntounidimensionaldedadosinseparável

Édifícil (e, provavelmente, umamá ideia) usarmáquinas de vetor de suportesemdependerdeumsoftwareespecializadoemotimizaçãoescritoporpessoascomoconhecimentoapropriado,entãovamosdeixarnossotratamentoaqui.

Page 283: Data Science do zero: Primeiras regras com o Python

Figura16-8.Oconjuntodedadossetornaseparávelemdimensõesmaiores

Page 284: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosscikit-learnpossuimodelosparaRegressãoLogística(http://bit.ly/1xkbywA)eMáquinasdeVetordeSuporte(http://bit.ly/1xkbBZj).libsvm(http://bit.ly/1xkbA7t)éaimplementaçãodemáquinadevetordesuportequescikit-learnusa.Emseuwebsiteháumavariedadededocumentaçãosobremáquinasdevetordesuporte.

Page 285: Data Science do zero: Primeiras regras com o Python

CAPÍTULO17

ÁrvoresdeDecisão

Umaárvoreéummistérioincompreensível.—JimWooddring

O vice-presidente de Talentos da DataSciencester entrevistou um número decandidatosparaempregodosite,comníveisdesucessovariados.Elecoletouumconjunto de dados com vários atributos (qualitativos) de cada candidato, bemcomoseocandidatodesaiubemoumalnaentrevista.Vocêpoderiausaressesdados para construir um modelo identificando quais candidatos farão boasentrevistas,paraqueelenãopreciseperdertempofazendoentrevistas?

Isso parece ser perfeito para uma árvore de decisão, outra ferramente demodelagemdeprevisãonokitdeumcientistadedados.

Page 286: Data Science do zero: Primeiras regras com o Python

•••••••••

OQueÉumaÁrvoredeDecisão?Umaárvorededecisãousaumaestruturadeárvorepararepresentarumnúmerodepossíveiscaminhosdedecisãoeumresultadoparacadacaminho.

SevocêjájogouVintePerguntas,jáestáfamiliarizadocomárvoresdedecisão.Porexemplo:

“Estoupensandoemumanimal.”“Elepossuimaisdecincopernas?”“Não.”“Édelicioso?”“Não.”“Eleaparecenapartedetrásdamoedadecincocentavosaustraliana?”“Sim.”“Éumequidna?”“Sim!”

Issocorrespondeaocaminho:

“Nãomaisdoque5pernas”→“Nãodelicioso”→“Namoedade5centavos”→“Equidna!”

emumaidiossincrática(enãomuitoabrangente)árvorededecisão“adivinheoanimal”(Figura17-1).

Page 287: Data Science do zero: Primeiras regras com o Python

Figura17-1.Umaárvorededecisão“adivinheoanimal”

Asárvoresdedecisãopossuemmuitasrecomendações.Elassãomuitofáceisdeentender e interpretar, e o processo por onde chegam numa previsão écompletamente transparante.Diferentedeoutrosmodelosquevimosatéagora,as árvores de decisão podem lidar facilmente com uma mistura de atributosnuméricos (exemplo:númerodepernas) e categóricos (exemplo:delicioso/nãodelicioso)epodematéclassificarosdadosparaosatributosqueestãofaltando.

Aomesmo tempo, encontrar a árvorededecisãoperfeitaparaumconjuntodedados em treinamento é computacionalmente um problema muito difícil.(Contornaremos isso tentando construir uma árvore boa o bastante em vez deumaperfeita,apesardeque,paraumaboapartedeconjuntosdedadosissoaindapode ser muito trabalhoso.) Mais importante, é muito fácil (e muito ruim)construirárvoresdedecisãoquesãosobreajustadasaosdadosemtreinamentoeque não generalizem bem para dados desconhecidos. Nós veremos formas delidarcomisso.

Amaioriadaspessoasdividemárvoresdedecisãoemárvoresdeclassificação(queproduzemsaídascategóricas)eárvoresderegressão(queproduzemsaídas

Page 288: Data Science do zero: Primeiras regras com o Python

numéricas). Neste capítulo, focaremos em árvores de classificação etrabalharemoscomoalgoritmoID3paraaprenderumaárvorededecisãoapartirde um conjunto de dados rotulados, o que talvez nos ajude a entender comoárvores de decisão realmente funcionam. Para simplificar as coisas, nosrestringiremosaproblemascomsaídasbináriascomo“eudeveriacontrataressecandidato?”ou“eudeveriaexibiroanúncioAouoBparavisitantesdosite?”ou“comeressacomidaqueencontreinageladeiradoescritóriomefarámal?”

Page 289: Data Science do zero: Primeiras regras com o Python

EntropiaPraconstruirumaárvorededecisão,precisaremosdecidirquaisperguntasfazereemqualordem.Emcadaetapadeumaárvoreháalgumaspossibilidadesqueeliminamos e outras que não. Após ter a informação de que um animal nãopossui mais do que cinco pernas, eliminamos a possibilidade de ele ser umgafanhoto. Não eliminamos a possibilidade de ser um pato. Cada perguntapossívelseparaaspossibilidadesrestantesdeacordocomasrespostas.

Nós gostaríamos de escolher perguntas cujas respostas nos dessem muitainformação sobre o que nossa árvore deveria prever. Se houver uma simplesperguntasim/nãoparacadarespostas“sim”semprecorrespondenteasaídasTrue(Verdadeiros)e respostas“não”asaídasFalse (Falsos), essa seriaumaperguntaperfeitaparafazer.Contrariamente,umaperguntasim/nãoparaaqualnenhumarespostalhedámuitainformaçãosobreoqueprevisãodeveriasernãoéumaboaescolha.

Nóschegamosnessanoçãode“quantainformação”comentropia.Vocêjádeveterescutadoissocomosignificadodedesordem.Nósusamospararepresentaraincertezaassociadacomosdados.

Imagineque temosumconjuntoS de dados, no qual cadamembro é rotuladocomopertencenteaumaclassedentreofinitonúmerodeclassesC1,…,Cn.Setodosospontosdedadospertencemaumaúnicaclasse,entãonãoháincertezareal,oquesignificaquedevehaverbaixaentropia.Seospontosdedadosestãoseparados de forma igual nas classes, há muita incerteza e deve haver altaentropia.

Emtermosmatemáticos,sepiéaproporçãodedadosdefinidoscomoclassesci,nósdefinimosaentropiacomo:

comaconvenção(padrão)deque0log0=0.

Semmuitapreocupaçãocomosdetalhes,cadatermo–pilog2piénãonegativoepróximodezeroprecisamentequandopiouépróximodezerooude1(Figura

Page 290: Data Science do zero: Primeiras regras com o Python

17-2).

Figura17-2.Umgráficode–plogp

Isso significaqueaentropia serápequenaquandocadapiépróximode0ou1(por exemplo: quando a maioria dos dados está em uma única classe) e serámaiorquandomuitosdospinãoestiverempróximosde0(porexemplo,quandoos dados estão espalhados por múltiplas classes). Esse é exatamente ocomportamentoesperado.

Émuitofáciljogartudoissoemumafunção:defentropy(class_probabilities):

"""dadaumalistadeprobabilidadesdeclasse,computeaentropia"""returnsum(-p*math.log(p,2)

forpinclass_probabilitiesifp)#ignoraprobabilidadeszero

Nossosdadosconsistirãodepares(input,label),oquesignificaqueprecisaremos

Page 291: Data Science do zero: Primeiras regras com o Python

computar sozinhos as probabilidades de classe. Observe que não nos importaqual rótulo é associada com qual probabilidade, apenas quais são asprobabilidades:

defclass_probabilities(labels):total_count=len(labels)return[count/total_count

forcountinCounter(labels).values()]

defdata_entropy(labeled_data):labels=[labelfor_,labelinlabeled_data]probabilities=class_probabilities(labels)returnentropy(probabilities)

Page 292: Data Science do zero: Primeiras regras com o Python

AEntropiadeumaPartiçãoO que fizemos até agora foi computar a entropia (pense “incerteza”) de umconjunto de dados rotulados. Agora, cada etapa da árvore de decisão envolvefazer uma pergunta cuja resposta particiona os dados em um ou (esperamos)mais subconjuntos. Por exemplo, nossa pergunta “temmais de cinco pernas?”divideanimaisemaquelesquetêmmaisdecincopernas(porexemplo,aranhas)eosquenão(porexemplo,equidnas).

Damesmaforma,nósgostaríamosdeteralgumanoçãodeentropiaqueresulteemparticionarumconjuntodedadosemumacertaforma.Nósqueremosumadivisão que tenha baixa entropia se dividir os dados em subconjuntos quetenham baixa entropia (por exemplo, são altamente certos) e alta entropia sepossuir subconjuntos com (grandes e com) alta entropia (por exemplo, sãoaltamenteincertos).

Porexemplo,minhapergunta“moedadecincocentavosaustraliana”foimuitoboba (e bem sortuda!), à medida que dividiu os animais restantes até aquelemomentoemS1={equidna}eS2={orestante},emqueS2égrandeepossuialta entropia. (S1 não tem entropia mas representa uma pequena fração das“classes”restantes.)

Matematicamente, se dividirmos nossos dados S em subconjuntos S1,…,Smcontendoproporçõesdedadosq1,…,qm, entãonóscomputamosa entropiadapartiçãocomoumasomaponderada:

quepodemosimplementarcomo:defpartition_entropy(subsets):

"""encontreaentropiadestadivisãodedadosemsubconjuntossubconjuntoéumalistadelistasdedadosrotulados"""

total_count=sum(len(subset)forsubsetinsubsets)

returnsum(data_entropy(subset)*len(subset)/total_countforsubsetinsubsets)

Umproblemacomessaabordageméqueparticionarporumatributo(característica)

Page 293: Data Science do zero: Primeiras regras com o Python

com muitos valores diferentes resultará em um entropia muito baixa devido aosobreajuste.Porexemplo,imaginequevocêtrabalhaparaumbancoeestátentandoconstruir uma árvore de decisão para prever quais clientes provavelmente serãoinadimplentes com o financiamento usando alguns dados históricos como seuconjuntode treinamento. ImagineaindaqueosdadoscontêmonúmerodoCPFdecada cliente.Dividir emSSNproduzirá subconjuntosdeumapessoa, emque cadauma delas necessariamente possui zero entropia.Mas ummodelo que depende deSSN certamente não generaliza além do conjunto de treinamento. Por isso, vocêdeveriaevitar(ouagrupar,seapropriado)atributoscommuitosvalorespossíveisaocriarárvoresdedecisão.

Page 294: Data Science do zero: Primeiras regras com o Python

•••

CriandoumaÁrvoredeDecisãoAvice-presidente forneceudadosdosentrevistados,queconsistemde (por suaespecificação)pares(input,label)emquecada inputéumdictdecaracterísticasdecandidatos e cada rótulo é True (o candidato fez boa entrevista) ou False (ocandidato fez entrevista ruim). Em específico, você possui o nível de cadacandidato,sualinguagemfavorita,seéativonoTwitteresepossuiPhD:

inputs=[({'level':'Senior','lang':'Java','tweets':'no','phd':'no'},False),({'level':'Senior','lang':'Java','tweets':'no','phd':'yes'},False),({'level':'Mid','lang':'Python','tweets':'no','phd':'no'},True),({'level':'Junior','lang':'Python','tweets':'no','phd':'no'},True),({'level':'Junior','lang':'R','tweets':'yes','phd':'no'},True),({'level':'Junior','lang':'R','tweets':'yes','phd':'yes'},False),({'level':'Mid','lang':'R','tweets':'yes','phd':'yes'},True),({'level':'Senior','lang':'Python','tweets':'no','phd':'no'},False),({'level':'Senior','lang':'R','tweets':'yes','phd':'no'},True),({'level':'Junior','lang':'Python','tweets':'yes','phd':'no'},True),({'level':'Senior','lang':'Python','tweets':'yes','phd':'yes'},True),({'level':'Mid','lang':'Python','tweets':'no','phd':'yes'},True),({'level':'Mid','lang':'Java','tweets':'yes','phd':'no'},True),({'level':'Junior','lang':'Python','tweets':'no','phd':'yes'},False)

]

Nossa árvore consistirá de nós de decisão (que fazem uma pergunta edirecionamdeformadiferentedependendodaresposta)enósfolha(quenosdãouma previsão). Nós a construiremos usando um algoritmo ID3 relativamentesimples que opera da seguinte forma. Digamos que nos deram alguns dadosrotuladoseumalistadecaracterísticasquedeveríamosconsiderarpararamificar.

Seosdadospossuemamesmorótulo,crieumnófolhaqueprevêesserótuloeentãopare.Sealistadecaracterísticasestávazia(porexemplo:nãoexistemmaisperguntaspossíveis),crieumnófolhaqueprevêorótulomaiscomumepare.Casocontrário,tenteparticionarosdadosportodasascaracterísticas.Escolhaadivisãocomaentropiadepartiçãomaisbaixa.Adicioneumnódedecisãobaseadonacaracterísticaescolhida.

Page 295: Data Science do zero: Primeiras regras com o Python

• Retorneacadasubconjuntoparticionadousandoascaracterísticasremanescentes.

Isso é conhecido como um algoritmo “ganancioso” porque, a cada passo, eleescolheaopçãoimediatamentemelhor.Dadoumconjuntodedados,podeexistirumaárvoremelhorcomumprimeiromovimentopiordever.Casoexista,essealgoritmonãoiráencontrá-la.Contudo,érelativamentemaisfácildeentendereimplementar, o que o torna muito bom para começar a explorar árvores dedecisão.

Vamos percorrer manualmente esses passos no conjunto de dados dosentrevistados.OconjuntodedadospossuirótulosTrueeFalse,enóstemosquatrocaracterísticaspelasquaispodemosdividi-los.Então,nossoprimeiropassoseráencontrar a partição com a menor entropia. Começaremos escrevendo umafunçãoquefazadivisão:

defpartition_by(inputs,attribute):"""cadaentradaéumpar(attribute_dict,label).retornaumadict:attribute_value->inputs"""groups=defaultdict(list)forinputininputs:key=input[0][attribute]#pegaovalordoatributoespecificadogroups[key].append(input)#entãoadicionaessaentradaàlistacorretareturngroups

eumaqueusaissoparacomputaraentropia:defpartition_entropy_by(inputs,attribute):

"""computaaentropiacorrespondenteàpartiçãodada"""partitions=partition_by(inputs,attribute)returnpartition_entropy(partitions.values())

Entãosóprecisamosencontrarapartiçãocomentropiamínimaparatodooconjuntodedados:

forkeyin['level','lang','tweets','phd']:printkey,partition_entropy_by(inputs,key)

#level0.693536138896#lang0.860131712855#tweets0.788450457308#phd0.892158928262

Amenorentropiavemdadivisãobaseadaem level,entãoprecisamosfazeruma

Page 296: Data Science do zero: Primeiras regras com o Python

sub-árvoreparacadavalorlevelpossível.CadacandidatoMidérotuladocomTrue,oquesignificaqueasub-árvoreMidésimplesmenteumnófolhaqueprevêTrue.Para candidatos Senior, temos uma mistura de Trues e Falses, logo precisamosdividirnovamente:

senior_inputs=[(input,label)forinput,labelininputsifinput["level"]=="Senior"]

forkeyin['lang','tweets','phd']:printkey,partition_entropy_by(senior_inputs,key)

#lang0.4#tweets0.0#phd0.950977500433

Issonosmostraquenossapróximadivisãodeveriasercombaseem tweets,queresultaemumapartiçãodeentropiazero.ParaoscandidatosSenior,tweets“sim”sempreresultamemTrueenquantotweets“não”sempreresultamemFalse.

Finalmente,sefizermosamesmacoisaparaoscandidatosJunior,dividiremosemphd,apósoquedescobrimosquenãoPhDsempreresultaemTrueePhDsempreresultaemFalse.

AFigura17-3mostraaárvorededecisãocompleta.

Page 297: Data Science do zero: Primeiras regras com o Python

Figura17-3.Aárvorededecisãoparacontratação

Page 298: Data Science do zero: Primeiras regras com o Python

•••

JuntandoTudoAgoraquevimoscomooalgoritmofunciona,gostaríamosdeimplementá-lodeforma mais geral. Isso significa que precisamos decidir como queremosrepresentar as árvores. Usaremos a representação mais leve possível. Nósdefinimosasárvorescomo:

True

False

umatupla(attribute,subtree_dict)

Aqui True representa um nó folha que retorna True para qualquer entrada, Falserepresenta um nó folha que retorna False para qualquer entrada, e uma tuplarepresenta um nó de decisão que, para qualquer entrada, encontra seu valorattributeeclassificaaentradausandoasub-árvorecorrespondente.

Comessarepresentação,nossaárvoredecontrataçãosepareceriacomisso:('level',{'Junior':('phd',{'no':True,'yes':False}),'Mid':True,'Senior':('tweets',{'no':False,'yes':True})})

Ainda há a questão do que fazer se encontrarmos um valor de característicainesperada (ou faltante). O que nossa árvore deveria fazer se encontrasse umcandidatocujolevelé“estagiário”?Lidaremoscomessecasoacrescentandoumachave None que prevê o rótulo mais comum. (Apesar de que isso seria umapéssimaideiaseNonefossenaverdadeumvalorqueaparecessenosdados.)

Dadatalrepresentação,podemosclassificarumaentradacom:defclassify(tree,input):

"""classificaaentradausandoaárvorededecisãofornecida"""

#seforumnófolha,retornaseuvaloriftreein[True,False]:returntree

#senão,estaárvoreconsistedeumacaracterísticaparadividir#eumdicionáriocujaschavessãovaloresdaquelacaracterística#ecujosvaloressãosub-árvoresparaconsiderardepoisattribute,subtree_dict=tree

Page 299: Data Science do zero: Primeiras regras com o Python

subtree_key=input.get(attribute)#Noneseestiverfaltandocaracterística

ifsubtree_keynotinsubtree_dict:#senãohásub-árvoreparachave,subtree_key=None#usaremosasub-árvoreNone

subtree=subtree_dict[subtree_key]#escolhaasub-árvoreapropriadareturnclassify(subtree,input)#euseparaclassificaraentrada

E tudo o que restou é construir a representação da árvore a partir dos nossosdadosemtreinamento:

defbuild_tree_id3(inputs,split_candidates=None):

#seesteénossoprimeiropasso,#todasaschavesdaprimeiraentradasãocandidatosdivididos

ifsplit_candidatesisNone:split_candidates=inputs[0][0].keys()

#contaTrueseFalsesnasentradasnum_inputs=len(inputs)num_trues=len([labelforitem,labelininputsiflabel])num_falses=num_inputs-num_trues

ifnum_trues==0:returnFalse#nenhumTrue?Retorneumafolha“False”ifnum_falses==0:returnTrue#nenhumFalse?Retorneumafolha“True”

ifnotsplit_candidates:#senãohouvermaiscandidatosadividirreturnnum_trues>=num_falses#retorneafolhamajoritária

#senão,dividacombasenamelhorcaracterísticabest_attribute=min(split_candidates,

key=partial(partition_entropy_by,inputs))

partitions=partition_by(inputs,best_attribute)new_candidates=[aforainsplit_candidates

ifa!=best_attribute]

#recursivamenteconstróiassub-árvoressubtrees={attribute_value:build_tree_id3(subset,new_candidates)

forattribute_value,subsetinpartitions.iteritems()}

subtrees[None]=num_trues>num_falses#casopadrão

return(best_attribute,subtrees)

Naárvorequeconstruímos,cadafolhaconsistiainteiramentedeentradasTrueouinteiramentedeentradasFalse.Issosignificaqueaárvoreprevêperfeitamenteemumconjuntodedadosemtreinamento.Masnóstambémpodemosaplicarissoanovosdadosquenãoestavamnoconjuntodetreinamento:

tree=build_tree_id3(inputs)

classify(tree,{"level":"Junior",

Page 300: Data Science do zero: Primeiras regras com o Python

"lang":"Java","tweets":"yes","phd":"no"})#True

classify(tree,{"level":"Junior","lang":"Java","tweets":"yes","phd":"yes"})#False

Etambémemdadoscomvaloresfaltandoouinesperados:classify(tree,{"level":"Intern"})#Trueclassify(tree,{"level":"Senior"})#False

Comonossoobjetivoerademonstrarcomoconstruirumaárvore,nósconstruímosaárvore usado todo o conjunto de dados. Como sempre, se realmente estivéssemostentandocriarumbommodeloparaalgumacoisa,nósteríamos(coletadomaisdadose)divididoosdadosemsubconjuntosdetreinamento/validação/teste.

Page 301: Data Science do zero: Primeiras regras com o Python

FlorestasAleatóriasLevando em consideração como árvores de decisão podem se ajustar quaseperfeitamenteaseusdadosemtreinamento,nãonossurpreendequeelastendema sobreajustar. Uma forma de evitar isso é a técnica chamada florestasaleatórias, na qual podemos construir várias árvores de decisão e deixá-lasescolhercomoclassificarentradas:

defforest_classify(trees,input):votes=[classify(tree,input)fortreeintrees]vote_counts=Counter(votes)returnvote_counts.most_common(1)[0][0]

Nossoprocessodeconstruçãodeárvoreseradeterminista,entãocomoconseguimosárvoresaleatórias?

Uma parte envolve dados inicialização (lembre-se de “Digressão: AInicialização”, na página 183). Em vez de treinar cada árvore em todas asentradasnoconjuntodetreinamento,nóstreinamoscadaárvorenoresultadodebootstrap_sample(inputs). Uma vez que cada árvore é construída usando dadosdiferentes,cadaárvoreserádiferentedaoutra.(Umbenefícioéqueusardadosnão-amostradospara testarcadaárvoreéummétodojusto,oquesignificaquevocêpodecontinuarusandotodososdadoscomooconjuntodetreinamentosevocê souber medir rendimento.) Esta técnica é conhecida como bootstrapaggregatingoubagging(empacotamento).

Umasegundaformaenvolvemudarcomoescolhemosaformacomobest_attributedivide-se.Emvezdeolharparatodososatributosremanescentes,nósprimeiroescolhemosumsubconjuntoaleatórioeodividimosnoqueformelhor:

#sejáhácandidatosobastante,olheparatodoselesiflen(split_candidates)<=self.num_split_candidates:

sampled_split_candidates=split_candidates#senãoescolhaumaamostraaleatóriaelse:

sampled_split_candidates=random.sample(split_candidates,self.num_split_candidates)

#agoraescolhaamelhorcaracterísticaapartirapenasdaquelescandidatosbest_attribute=min(sampled_split_candidates,

Page 302: Data Science do zero: Primeiras regras com o Python

key=partial(partition_entropy_by,inputs))

partitions=partition_by(inputs,best_attribute)

Esse é um exemplo e uma técnicamais ampla chamadaensemble learning naqual combinamos vários weak learners (tipicamente modelos de altapolarização,ebaixavariância)paraproduzirummodeloforteglobal.

Florestasaleatóriassãoumdosmodelosmaispopulareseversáteisdisponíveis.

Page 303: Data Science do zero: Primeiras regras com o Python

ParaMaioresEsclarecimentosscikit-learnpossuimuitosmodelosdeÁrvoresdeDecisão(http://bit.ly/1ycPmuq).Tambémpossuiummóduloensemble(http://bit.ly/1ycPom1)queincluiRandomForestClassifiereoutrosmétodos.

Nósquasenãoarranhamosasuperfíciedotópicodeárvoresdedecisãoeseusalgoritmos.AWikipédia(http://bit.ly/1ycPn1j)éumbompontodepartidaparaumaexplicaçãomaisampla.

Page 304: Data Science do zero: Primeiras regras com o Python

CAPÍTULO18

RedesNeurais

Eugostodecoisassemsentido;elasacordamascélulasdocérebro.—Dr.Seuss

Umaredeneuralartificial(ouredeneural)éummodelopreditivomotivadopelaforma como o cérebro funciona. Pense no cérebro como uma coleção deneurôniosconectados.Cadaneurônioolhaparaasaídadeoutrosneurôniosqueo alimentam, faz um cálculo e então ele dispara (se o cálculo exceder algumlimite)ounão(senãoexceder).

Redes neurais artificiais consistem de neurônios artificiais, que desenvolvemcálculos similares sobre suas entradas. Redes neurais podem resolver umavariedadedeproblemascomoreconhecimentodecaligrafiaedetecçãofacial,eelas são muito usadas em deep learning (aprendizado profundo), uma dassubáreasmaispopularesdedatascience.Entretanto,amaioriadasredesneuraissão “caixas-pretas” — inspecionar seus detalhes não lhe fornece muitoentendimento de como elas estão resolvendo um problema. E grandes redesneurais podem ser difíceis de treinar. Para amaioria dos problemas que vocêencontrará como um cientista de dados, elas provavelmente não são amelhoropção. Algum dia, quando você estiver tentando construir uma inteligênciaartificialparatornarrealaSingularidade,elaspodemser.

Page 305: Data Science do zero: Primeiras regras com o Python

PerceptronsA rede neuralmais simples é a perceptron, que aproxima um único neurôniocom n entradas binárias. Ela computa a soma ponderada de suas entradas e“dispara”seessasomaforzerooumaior:

defstep_function(x):return1ifx>=0else0

defperceptron_output(weights,bias,x):"""retorna1seaperceptron'disparar',0senão"""calculation=dot(weights,x)+biasreturnstep_function(calculation)

Perceptronésimplesmenteadistinçãoentreespaçosseparadospelohiperplanodepontosx,peloqual:

dot(weights,x)+bias==0

Com pesos propriamente escolhidos, perceptrons podem solucionar algunsproblemassimples (Figura18-1).Porexemplo,podemoscriarumaportaAND(queretorna1seambasentradasforem1masretorna0seumadasentradasfor0)com:

weights=[2,2]bias=-3

Seambasasentradasforem1,ocálculo(calculation)seráiguala2+2–3=1,easaídaserá1.Seapenasumadasentradasfor1,ocálculoseráiguala2+0–3=–1,easaídaserá0.Eseambasentradasforem0,ocálculoserá–3easaídaserá0.

Similarmente,poderíamosconstruirumaportaORcom:weights=[2,2]bias=-1

Page 306: Data Science do zero: Primeiras regras com o Python

Figura18-1.Espaçodedecisãoparaumperceptrondeduasentradas

EpoderíamosconstruirumaportaNOT (que teriaumaentradaeconverteria1para0e0para1)com:

weights=[-2]bias=1

Entretanto, existem alguns problemas que simplesmente não podem serresolvidoscomapenasumperceptron.Porexemplo,nãoimportaoquantovocêtente, você não pode usar umperceptron para construir um aportaXOR comsaída 1 se exatamente uma de suas entradas for 1 ou então 0. É aí quecomeçamosaprecisarderedesneuraismaiscomplicadas.

Claro, você não precisa da aproximação de um neurônio para construir umaportalógica:

and_gate=minor_gate=maxxor_gate=lambdax,y:0ifx==yelse1

Page 307: Data Science do zero: Primeiras regras com o Python

Comoneurônios reais,neurôniosartificiaiscomeçamaficarmais interessantesquandovocêcomeçaaconectá-los.

Page 308: Data Science do zero: Primeiras regras com o Python

RedesNeuraisFeed-ForwardAtopologiadocérebroédemasiadamentecomplicada,entãoénormalaproximá-la com uma rede neural feed-forward idealizada que consiste de camadasdiscretasdeneurônios,cadaumaconectadaàseguinte.Issotipicamenteenvolveumacamadadeentrada(querecebeentradaseastransmitesemmodificações),uma oumais “camadas ocultas” (emque cada uma consiste de neurônios quepegam saídas da camada anterior, fazem algum cálculo e passam o resultadoparaapróximacamada),eumacamadadesaída(queproduzassaídasfinais).

Assim como o perceptron, cada neurônio (não de entrada) possui o pesocorrespondenteacadaumadesuasentradaseumapolarização(tendência).Parasimplificarnossa representação,adicionaremosapolarização (bias)no finaldonossovetordepesosedaremosacadaneurônioumaentradapolarizadaqueésempreiguala1.

Como com o perceptron, para cada neurônio somaremos os produtos de suasentradas e seus pesos. Mas aqui, em vez de gerar step_function aplicada àqueleproduto,exibiremosumaaproximaçãosuavedafunçãostep.Usaremosafunçãosigmoid(Figura18-2):

defsigmoid(t):return1/(1+math.exp(-t))

Page 309: Data Science do zero: Primeiras regras com o Python

Figura18-2.Afunçãosigmoid

Porqueusar sigmoid emvezdeumamais simples step_function?Para treinarumarede neural, precisaremos usar cálculo, e para usar cálculo, precisaremos defunçõessuaves.Afunçãostepnãoécontínua,esigmoidéumaboaaproximaçãosuavedela.

Você deve se lembrar de sigmoid do Capítulo 16, onde era chamada de logistic.Tecnicamente, “sigmoid” se refere ao formato da função, “logística” a esta funçãoespecíficaemboraaspessoasgeralmenteusemostermosindistintamente.

Nóspodemosentãocalcularasaídacomo:defneuron_output(weights,inputs):

returnsigmoid(dot(weights,inputs))

Dada essa função, nós podemos representar um neurônio simplesmente como

Page 310: Data Science do zero: Primeiras regras com o Python

uma listadepesoscujo tamanhoémaisdoqueonúmerodeentradasdaqueleneurônio(porcausadopesobias).Então,podemosrepresentarumaredeneuralcomoumalistadecamadas(nãodeentrada),emquecadacamadaéapenasumalistadeneurôniosnaquelacamada.

Isto é, representaremos uma rede neural como uma lista (camadas) de listas(neurônios)delistas(pesos).

Dadatalrepresentação,usararedeneuralébemsimples:deffeed_forward(neural_network,input_vector):

"""recebearedeneural(representadacomoumalistadelistasdelistasdepesos)eretornaasaídaapartirdaentradaasepropagar"""

outputs=[]

#processaumacamadaporvezforlayerinneural_network:

input_with_bias=input_vector+[1]#adicionaumaentradapolarizadaoutput=[neuron_output(neuron,input_with_bias)#computaasaída

forneuroninlayer]#paracadaneurôniooutputs.append(output)#ememoriza

#entãoaentradaparaapróximacamadaéasaídadestainput_vector=output

returnoutputs

AgoraéfácilconstruiraportaXORquenãopodíamosconstruircomumúnicoperceptron. Só precisamos ajustar os pesos para que neuron_outputs seja bempróximode0oude1:

xor_network=[#camadaoculta[[20,20,-30],#neurônio'and'[20,20,-10]],#neurônio'or'#outputlayer[[-60,60,-30]]]#neurônio'segundaentrada,#masnãoaprimeiraentrada'

forxin[0,1]:foryin[0,1]:

#feed_forwardproduzassaídasparatodososneurônios#feed_forward[-1]éasaídadacamadadesaídadeneurôniosprintx,y,feed_forward(xor_network,[x,y])[-1]

#00[9.38314668300676e-14]#01[0.9999999999999059]

Page 311: Data Science do zero: Primeiras regras com o Python

#10[0.9999999999999059]#11[9.383146683006828e-14]

Aousarumacamadaoculta,podemostransmitirasaídadeumneurônio“and”easaídadeumneurônio“or”emumneurônio“segundaentradamasnãoprimeiraentrada”. O resultado é uma rede que realiza “or, mas não and”, que éprecisamenteXOR(Figura18-3).

Figura18-3.UmaredeneuralparaXOR

Page 312: Data Science do zero: Primeiras regras com o Python

1.

2.

3.

4.

5.

BackpropagationGeralmente nós não construímos redes neurais manualmente. Isso se dá, emparte,porqueasusamospararesolverproblemasmuitomaiores—umproblemadereconhecimentodaimagempodeenvolverdezenasoumilharesdeneurônios.E em parte porque nós geralmente não conseguimos “raciocinar” sobre o queneurôniosdeveriamser.

Em vez disso, nós usamos dados para treinar redes neurais. Uma abordagempopularéumalgoritmochamadobackpropagationquepossuisemelhançascomoalgoritmogradientedescendentequevimosanteriormente.

Imagine que temos um conjunto de treinamento que consiste de vetores deentradas e correspondentes vetores alvos de saída. Por exemplo, em nossoexemploanteriorxor_network,ovetordeentrada[1,0]correspondiaaoalvodesaída[1].E imaginequenossa rede temalgumconjuntodepesos.Nósajustamosospesosusandooseguintealgoritmo:

Executefeed_forwardemumvetordeentradaparaproduzirsaídasde todososneurôniosnarede.Issoresultaemumerroparacadaneurôniodesaída—adiferençaentresuasaídaeseualvo.Compute o gradiente para esse erro como uma função de pesos deneurônioseajusteseuspesosnadireçãoquemaisdiminuioerro.“Propague”esseserrosdesaídadevoltaparainferirerrosparaascamadasocultas.Computeosgradientesdesseserroseajusteospesosdacamadaocultadamesmamaneira.

Tipicamente, nós executamos o algoritmo muitas vezes para todo o nossoconjuntodetreinamentoatéquearedeconvirja:

defbackpropagate(network,input_vector,targets):

hidden_outputs,outputs=feed_forward(network,input_vector)

#asaída*(1–output)édaderivadadasigmoidoutput_deltas=[output*(1-output)*(output-target)

foroutput,targetinzip(outputs,targets)]

Page 313: Data Science do zero: Primeiras regras com o Python

#ajustaospesosparaacamadadesaída,umneurônioporvezfori,output_neuroninenumerate(network[-1]):

#focanoi-ésimoneurôniodacamadadesaídaforj,hidden_outputinenumerate(hidden_outputs+[1]):

#ajustaoj-ésimopesobaseadoemambos#odeltadesteneurônioesuaj-ésimaentradaoutput_neuron[j]-=output_deltas[i]*hidden_output

#errosdebackpropagationparaacamadaocultahidden_deltas=[hidden_output*(1-hidden_output)*

dot(output_deltas,[n[i]forninoutput_layer])fori,hidden_outputinenumerate(hidden_outputs)]

#ajustaospesosparaacamadaoculta,umneurônioporvezfori,hidden_neuroninenumerate(network[0]):

forj,inputinenumerate(input_vector+[1]):hidden_neuron[j]-=hidden_deltas[i]*input

Isso é praticamente escrever explicitamente o erro ao quadrado como umafunçãodepesoseusarafunçãominimize_stochasticqueconstruímosnoCapítulo8.

Nestecaso,escreverexplicitamenteafunçãogradienteacabasendoumtipodedor. Se você sabe cálculo e a regra da cadeia, os detalhes matemáticos sãorelativamentediretos,masmanteranotaçãodireta(“aderivadaparcialdafunçãodeerrodopesoqueaqueleneurônioiatribuiàentradavindadoneurônioj”)nãoétãodivertido.

Page 314: Data Science do zero: Primeiras regras com o Python

Exemplo:DerrotandoumCAPTCHAPara certificar que pessoas que estão se registrando em seu site são realmentepessoas, a vice-presidente daGerência deProdutos quer quevocê implementeumCAPTCHA(CompletelyAutomatedPublicTuringtesttotellComputersandHumansApart)comopartedoprocessoderegistro.Emparticular,elegostariade exibir aos usuários uma imagem de um dígito e exigir que eles forneçamaqueledígitoparaprovarquesãohumanos.

Ele não acreditou quando você disse que computadores podem facilmenteresolver esse problema, então você decide convencê-lo criando um programaquefaçaisso.

Representaremoscadadígitocomoumaimagem5×5:

Nossa rede neural quer que uma entrada seja um vetor de números. Entãotransformaremoscadaimagememumvetordetamanho25,cujoselementossão1(“estepixelestánaimagem”)ou0(“estepixelnãoestánaimagem”).

Porexemplo,odígitozeroseriarepresentadocomo:zero_digit=[1,1,1,1,1,

1,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,1,1,1,1]

Nós queremos que nossa saída indique qual dígito a rede neural pensa que é,entãoprecisaremosde10saídas.Asaídacorretaparaodígito4,porexemplo,seria:

[0,0,0,0,1,0,0,0,0,0]

Então,presumindoquenossasentradasestãoordenadascorretamentede0a9,nossosalvosserão:

Page 315: Data Science do zero: Primeiras regras com o Python

targets=[[1ifi==jelse0foriinrange(10)]forjinrange(10)]

paraque(porexemplo)targets[4]sejaasaídacorretaparaodígito4.

Nessepontoestamosprontosparaconstruirnossaredeneural:random.seed(0)#parapegarresultadosrepetidosinput_size=25#cadaentradaéumvetordetamanho25num_hidden=5#teremos5neurôniosnacamadaocultaoutput_size=10#precisamosde10saídasparacadaentrada

#cadaneurônioocultotemumpesoporentrada,maisumpesobiashidden_layer=[[random.random()for__inrange(input_size+1)]

for__inrange(num_hidden)]

#cadaneurôniodesaídatemumpesoporneurôniooculto,maisopesobiasoutput_layer=[[random.random()for__inrange(num_hidden+1)]

for__inrange(output_size)]

#aredecomeçacompesosaleatóriosnetwork=[hidden_layer,output_layer]

Epodemostreinaroalgoritmobackpropagation:#10.000iteraçõesparecemserosuficienteparaconvergirfor__inrange(10000):

forinput_vector,target_vectorinzip(inputs,targets):backpropagate(network,input_vector,target_vector)

Issofuncionabemnoconjuntodetreinamento,obviamente:defpredict(input):

returnfeed_forward(network,input)[-1]

predict(inputs[7])#[0.026,0.0,0.0,0.018,0.001,0.0,0.0,0.967,0.0,0.0]

O que indica que a saída de neurônio de dígito 7 produz 0,97, enquanto quetodasasoutrassaídasdeneurôniosproduzemnúmerosmuitopequenos.

Mastambémpodemosaplicarissoadígitosdesenhadosdiferentes,comomeu3estilizado:

predict([0,1,1,1,0,#.@@@.0,0,0,1,1,#...@@0,0,1,1,0,#..@@.0,0,0,1,1,#...@@0,1,1,1,0])#.@@@.

#[0.0,0.0,0.0,0.92,0.0,0.0,0.0,0.01,0.0,0.12]

Page 316: Data Science do zero: Primeiras regras com o Python

Aredeaindapensaqueeleparececomum3,enquantomeu8estilizadorecebevotosparaserum5,um8eum9:

predict([0,1,1,1,0,#.@@@.1,0,0,1,1,#@..@@0,1,1,1,0,#.@@@.1,0,0,1,1,#@..@@0,1,1,1,0])#.@@@.

#[0.0,0.0,0.0,0.0,0.0,0.55,0.0,0.0,0.93,1.0]

Terumconjuntodetreinamentomaiorprovavelmenteajudaria.

Embora a operação da rede não seja exatamente transparente, podemosinspecionarospesosdacamadaocultaparaentenderoqueestãoreconhecendo.Podemos assinalar os pesos para cada neurônio como uma grade 5 × 5correspondenteàsentradas5×5.

Navidareal,vocêprovavelmentemarcariapesoszerocomobrancos,compesosmaiores positivos mais e mais (digamos) verdes e negativos com (digamos)vermelho.Infelizmente,émuitodifícilfazerissoemumlivropretoebranco.

Emvezdisso,marcaremospesoszerocombrancoepesosmaisemaisdistantesde zero cada vez mais escuros. E usaremos hachurado para indicar pesosnegativos.

Parafazerisso,usaremospyplot.imshow,quenãovimosantes.Comisso,podemosassinalar imagens pixel por pixel. Normalmente isso não é usado para datascience,masaquiéumaboaescolha:

importmatplotlibweights=network[0][0]#primeironeurônionacamadaocultaabs_weights=map(abs,weights)#aescuridãodependesomentedovalorabsoluto

grid=[abs_weights[row:(row+5)]#transformaospesosemumagrade5x5forrowinrange(0,25,5)]#[pesos[0:5],…,pesos[20:25]]

ax=plt.gca()#parausarhachuras,precisamosdeeixos

ax.imshow(grid,#aquiomesmoqueplt.imshowcmap=matplotlib.cm.binary,#useaescaladecorespretoebrancointerpolation='none')#assinalablocoscomoblocos

defpatch(x,y,hatch,color):"""retornaumobjetomatplotlib'patch'comalocalizaçãoespecificada,padrãodehachurasecor"""returnmatplotlib.patches.Rectangle((x-0.5,y-0.5),1,1,

Page 317: Data Science do zero: Primeiras regras com o Python

hatch=hatch,fill=False,color=color)

#hachuraspesosnegativosforiinrange(5):#linha

forjinrange(5):#colunaifweights[5*i+j]<0:#linhai,colunaj=pesos[5*i+j]

#adicionahachuraspretoebrancas,visíveissejamclarasouescurasax.add_patch(patch(j,i,'/',"white"))ax.add_patch(patch(j,i,'\\',"black"))

plt.show()

Figura18-4.Pesosparaacamadaoculta

Na Figura 18-4, podemos ver que o primeiro neurônio oculto possui grandespesospositivosnacolunadaesquerdaenocentrodafileiranomeio,enquantopossuigrandespesosnegativosnacolunadadireita.(Evocêpodeverquepossuigrandesbiasnegativos,oquesignificaquenãodispararáanãoserqueconsigaprecisamenteasentradaspositivasqueestá“procurando”.)

Semdúvidas,nessasentradas,elefazoqueesperamos:left_column_only=[1,0,0,0,0]*5printfeed_forward(network,left_column_only)[0][0]#1.0

center_middle_row=[0,0,0,0,0]*2+[0,1,1,1,0]+[0,0,0,0,0]*2printfeed_forward(network,center_middle_row)[0][0]#0.95

right_column_only=[0,0,0,0,1]*5printfeed_forward(network,right_column_only)[0][0]#0.0

Similarmente,oneurônioocultodomeioparece“gostar”de linhashorizontaismas não de linhas diagonais, e o último neurônio oculto parece “gostar” dafileiradocentromasnãodacolunadomeio. (Édifícilde interpretarosoutrosdoisneurônios.)

Oqueacontecequandoexecutamosmeu3estilizadonarede?

Page 318: Data Science do zero: Primeiras regras com o Python

my_three=[0,1,1,1,0,#.@@@.0,0,0,1,1,#...@@0,0,1,1,0,#..@@.0,0,0,1,1,#...@@0,1,1,1,0]#.@@@.

hidden,output=feed_forward(network,my_three)

Assaídashiddensão:0.121080#fromnetwork[0][0],provavelmenteexcedidopor(1,4)0.999979#fromnetwork[0][1],grandescontribuiçõesde(0,2)e(2,2)0.999999#fromnetwork[0][2],positivoemtodososlugaresmenos(3,4)0.999992#fromnetwork[0][3],maisumavezgrandescontribuiçõesde(0,2)e(2,2)0.000000#fromnetwork[0][4],negativoouzeroemtodososlugares,

menosnafileiradocentro

queentranoneurôniodesaída“three”(três)compesosnetwork[-1][3]:-11.61#pesoparaoculto[0]-2.17#pesoparaoculto[1]9.31#pesoparaoculto[2]-1.38#pesoparaoculto[3]-11.47#pesoparaoculto[4]-1.92#pesodaentradapolarizada

Demodoqueoneurôniocompute:sigmoid(.121*-11.61+1*-2.17+1*9.31-1.38*1-0*11.47-1.92)

queé0,92, comovimos.Naessência, acamadaocultaestácomputandocincodivisões diferentes de espaço dimensional 25, mapeando cada entradadimensional25paracinconúmeros.Eentãocadaneurôniodesaídaolhaapenasparaosresultadosdaquelascincodivisões.

Como vimos,my_three cai levemente na parte “inferior” da partição 0 (isto é,apenas ativa levemente o neurônio oculto 0), longe da parte “superior” daspartições1,2e3(istoé,ativafortementeaquelesneurôniosocultos),elongedaparteinferiordapartição4(istoé,nãoativanenhumneurônio).

Ecadaumdos10neurôniosde saídausaapenasaquelas cincoativaçõesparadecidirsemy_threeéseudígitoounão.

Page 319: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosACourseratemumcursogratuitosobreNeuralNetworksforMachineLearning(https://www.coursera.org/course/neuralnets).Oúltimocursofoiem2012,masosmateriaisdocursoaindaestãodisponíveis.MichaelNielsenestáescrevendoumlivroonlinegratuitosobreNeuralNetworksandDeepLearning(http://neuralnetworksanddeeplearning.com/).Quandovocêlerestelivro,elejádeveterterminado.PyBrain(http://pybrain.org)éumabibliotecaPythonsimplesderedeneural.Pylearn2(http://deeplearning.net/software/pylearn2/)éumabibliotecaderedeneuralmuitomaisavançada(emuitomaisdifícildeusar).

Page 320: Data Science do zero: Primeiras regras com o Python

CAPÍTULO19

Agrupamento

OndetínhamostaisagrupamentosNostornounobrementeselvagens,nãoinsanos

—RobertHerrick

Amaioria dos algoritmos neste livro são o que é conhecido por aprendizadosupervisionado, no que começam com um conjunto de dados rotulados e osusam como base para fazer previsões sobre novos dados, não rotulados.Agrupamento,entretanto,éumexemplodeaprendizadonãosupervisionado,emquenóstrabalhamoscomdadoscompletamentenãorotulados(ounoqualnossodadopossuirótulomasnósoignoramos).

Page 321: Data Science do zero: Primeiras regras com o Python

AIdeiaQuandovocêolhaparaalgumafontededadosénormalqueosdados,dealgumaforma, formem agrupamentos. Um conjunto de dados que mostre ondemilionários moram provavelmente possui agrupamentos em lugares comoBeverlyHillseManhattan.Umconjuntodedadosquemostrequantashorasaspessoas trabalham semanalmente provavelmente possui um agrupamento porvoltade40(esefortiradodeumestadocomleisexigindobenefíciosespeciaisparapessoasquetrabalhampelomenos20horasporsemana,provavelmenteteráoutro agrupamento por volta de 19). Um conjunto de dados demográficos deeleitoresregistradosprovavelmenteformaumavariedadedeagrupamentos(porexemplo: “mães de praticantes de futebol”, “aposentados entediados”, “jovensdesempregados”) que pesquisadores de opinião pública e consultores políticosdevemconsiderarrelevantes.

Diferentedealgunsdosproblemasquevimos,geralmentenãoháagrupamento“correto”. Um esquema de agrupamento alternativo pode agrupar alguns dos“jovens desempregados” com “estudantes de pós-graduação”, outros com“moradores do porão dos pais”. Nenhum esquema é necessariamente maiscorreto—pelo contrário, cadaumémelhornoquediz respeito à suaprópriamétrica“quãobonssãoosagrupamentos?”

Alémdisso,osagrupamentosnãoserotulamsozinhos.Vocêteráquefazerissovendoosdadoscontidosemcadaum.

Page 322: Data Science do zero: Primeiras regras com o Python

1.

2.3.

4.

OModeloPara nós, cada entrada (input) será um vetor em espaço dimensional d (querepresentaremos como uma lista de números). Nosso objetivo será identificaragrupamentos de entradas similares e,às vezes, encontrar um valorrepresentativoparacadaagrupamento.

Porexemplo,cadaentradapoderiaser(umvetornuméricoquedealgumaformarepresenta)otítulodeumpostdeumblog,emcujocasooobjetivopoderiaserencontrar agrupamentos de posts similares, talvez para entender sobre o quenossos usuários estão falando no blog. Ou imagine que temos uma imagemcontendomilharesdecores (red,green,blue)equenósprecisamostirarumacópiadeumaversãode10coresdela.Oagrupamentonosajudaaescolher10coresqueminimizarãoo“errodecor”total.

Umdosmétodosdeagrupamentomaissimpleséak-means,naqualumnúmerode agrupamentos ké escolhido antecipadamente, depois do que o objetivo éparticionar as entradas em conjuntos S1,…,Sk de uma forma queminimize asoma total das distâncias quadradas de cada ponto para a média de seuagrupamentodesignado.

Hámuitasformasdedefinirpontosnparaagrupamentosk,oquesignificaqueencontrar omelhor agrupamento é um problema bem difícil.Nós aceitaremosumalgoritmoiterativoqueusualmenteencontraumbomagrupamento:

Comece com um conjunto de k-means, que são pontos em espaçodimensionald.Associecadapontocomamédia(k-means)maispróxima.Senenhumaassociaçãodepontodeatribuiçãomudou,pareemantenhaosagrupamentos.Se alguma associação mudar, compute novamente as médias e volte aopasso2.

Usandoafunçãovector_meandoCapítulo4,ébemfácilcriarumaclassequefaçaisso:

classKMeans:

Page 323: Data Science do zero: Primeiras regras com o Python

"""executaagrupamentosk-means"""

def__init__(self,k):self.k=k#númerodeagrupamentosself.means=None#pontomédiodeagrupamentos

defclassify(self,input):"""retornaoíndicedoagrupamentomaispróximodaentrada"""returnmin(range(self.k),

key=lambdai:squared_distance(input,self.means[i]))

deftrain(self,inputs):#escolhapontoskaleatórioscomomédiainicialself.means=random.sample(inputs,self.k)assignments=None

whileTrue:#encontrenovasassociaçõesnew_assignments=map(self.classify,inputs)

#senenhumaassociaçãomudou,terminamos.ifassignments==new_assignments:return

#senão,mantenhaasnovasassociações,assignments=new_assignments

#ecomputenovasmédias,baseadonasnovasassociaçõesforiinrange(self.k):

#encontretodosospontosassociadosaoagrupamentoii_points=[pforp,ainzip(inputs,assignments)ifa==i]

#certifique-sequei_pointsnãoestávazio,#paranãodividirpor0ifi_points:self.means[i]=vector_mean(i_points)

Vamosvercomoissofunciona.

Page 324: Data Science do zero: Primeiras regras com o Python

Exemplo:EncontrosPara celebrar o crescimento da DataSciencester, a vice-presidente deRecompensasparaUsuárioquerorganizarváriosencontrospresenciaisparaosusuários de sua cidade natal, completos com cerveja, pizza e camisetasDataSciencester.Vocêsabealocalizaçãodetodososseususuárioslocais(Figura19-1),eelagostariaquevocêescolhesselocaisdeencontroparaquefiquemaisfácilparatodoscomparecerem.

Dependendo de como você enxerga, verá dois ou três agrupamentos. (É fácilfazer issovisualmenteporqueosdadosestãoapenasemduasdimensões.Commaisdimensões,seriamaisdifícildevisualizar.)

Primeiro imagine que ela possui orçamento o suficiente para três encontros.Vocêvaiatéseucomputadoretentaisso:

random.seed(0)#paraquevocêconsigaosmesmosclusterer=KMeans(3)#resultadosqueeuclusterer.train(inputs)printclusterer.means

Page 325: Data Science do zero: Primeiras regras com o Python

Figura19-1:Aslocalizaçõesdosusuáriosdesuacidadenatal

Vocêencontratrêsagrupamentoscentralizadosem[-45,4],[-16,10],e[18,20],evocêprocuralocaisdeencontropertodessaslocalizações(Figura19-2).

Você mostra isso à vice-presidente, que o informa que agora ela só temorçamentoparadoisencontros.

“Semproblemas”,vocêdiz:random.seed(0)clusterer=KMeans(2)clusterer.train(inputs)printclusterer.means

Page 326: Data Science do zero: Primeiras regras com o Python

Figura19-2:Aslocalizaçõesdeusuáriosagrupadasemtrêsagrupamentos

Como exibido na Figura 19-3, um encontro ainda deveria estar perto [18,20],masagoraooutrodeveestarperto[-26,-5].

Page 327: Data Science do zero: Primeiras regras com o Python

Figura19-3:Aslocalizaçõesdeusuáriosagrupadasemdoisagrupamentos

Page 328: Data Science do zero: Primeiras regras com o Python

EscolhendokNoexemploanterior,aescolhadekfoilevadaporfatoresforadonossocontrole.No geral, esse não seria o caso. Há uma grande variedade de caminhos paraescolher um k. Uma que é razoavelmente fácil de entender envolvemarcar asomadoserrosaoquadrado (entrecadapontoeamédiade seuagrupamento)comoumafunçãodekeolharparaondeográfico“dobra”:

defsquared_clustering_errors(inputs,k):"""encontraoerroaoquadradototaldek-meansagrupandoasentradas"""clusterer=KMeans(k)clusterer.train(inputs)means=clusterer.meansassignments=map(clusterer.classify,inputs)

returnsum(squared_distance(input,means[cluster])forinput,clusterinzip(inputs,assignments))

#agorafaçaográficode1atélen(inputs)agrupamentos

ks=range(1,len(inputs)+1)

errors=[squared_clustering_errors(inputs,k)forkinks]

plt.plot(ks,errors)plt.xticks(ks)plt.xlabel("k")plt.ylabel("totaldeerrosaoquadrado")plt.title("ErroTotalvs.NúmerodeAgrupamentos")plt.show()

Page 329: Data Science do zero: Primeiras regras com o Python

Figura19-4.Escolhendoumk

OlhandoparaaFigura19-4,essemétodocoincidecomsuavisãooriginalque3éonúmero“certo”deagrupamentos.

Page 330: Data Science do zero: Primeiras regras com o Python

1.2.

Exemplo:AgrupandoCoresA vice-presidente da Swag criou adesivos atraentes DataSciencester que elesgostariam que você entregasse nos encontros. Infelizmente, sua impressora deadesivos pode imprimir no máximo cinco cores por adesivo. E como a vice-presidente deArte está de licença, a vice-presidente daSwagperguntou se háalgumaformadevocêmodificarodesignparaquecontenhacincocores.

Asimagensdecomputadorpodemserrepresentadascomoumarraydepixelsdeduasdimensões,ondecadapixelpossuiumvetordetrêsdimensões(red,green,blue)indicandosuacor.

Criarumaversãodecincocoresdaimagemrequer:

EscolhercincocoresDesignarumadestascoresparacadapixel

Essa é uma excelente tarefa para agrupar a k-means, que pode particionar ospixelsemcincoagrupamentosemumespaçovermelho,verdeeazul.Seentãorecolorirmosospixelsemcadaagrupamentoparaacormédia,terminamos.

Para começar, precisaremos de uma maneira de carregar uma imagem emPython.Podemosfazerissocommatplotlib:

path_to_png_file=r"C:\images\image.png"#ondesuaimagemestáimportmatplotlib.imageasmpimgimg=mpimg.imread(path_to_png_file)

Portrásdascenas,imgéumarrayNumPy,masparanossosobjetivos,podemostratá-locomoumalistadelistasdelistas.

img[i][j]éopixelnai-ésimalinhaenacolunaj-ésima,ecadapixeléumalista[red, green, blue] de números entre 0 e 1 indicando a cor para aquele pixel(http://en.wikipedia.org/wiki/RGB_color_model):

top_row=img[0]top_left_pixel=top_row[0]red,green,blue=top_left_pixel

Emparticular,podemosconseguirumalistaestávelparatodosospixels,como:

Page 331: Data Science do zero: Primeiras regras com o Python

pixels=[pixelforrowinimgforpixelinrow]

eentãoabastecê-lasaonossoagrupamento:clusterer=KMeans(5)clusterer.train(pixels)#issopodedemorarumpouco

Uma vez terminado, apenas construímos uma imagem nova com o mesmoformato:

defrecolor(pixel):cluster=clusterer.classify(pixel)#índicedoagrupamentomaispróximoreturnclusterer.means[cluster]#pontomédiodoagrupamentomaispróximo

new_img=[[recolor(pixel)forpixelinrow]#recoloreestalinhadepixelsforrowinimg]#paracadalinhanaimagem

eexibeusandoplt.imshow():plt.imshow(new_img)plt.axis('off')plt.show()

Édifícilexibirosresultadosdecoresemumlivropretoebranco,masaFigura19-5mostraversõesemescaladecinzadeumaimagememcoreseasaídaparausaresseprocessoparareduzi-laparacincocores:

Figura19-5.Imagemoriginalesuadescoloraçãodemédia5

Page 332: Data Science do zero: Primeiras regras com o Python

1.2.

AgrupamentoHierárquicoBottom-upUmaabordagemalternativapara agrupamento é “criar” agrupamentosbottom-up.Podemosfazerissodaseguinteforma:

Façadecadaentradaseupróprioagrupamentodeum.Enquanto houver múltiplos agrupamentos sobrando encontre os doisagrupamentosmaispróximoseosjunte.

No final, teremos um agrupamento gigante contendo todas as entradas. Sequisermosacompanharaordemdejunção,podemosrecriarqualquernúmerodeagrupamentos desfazendo junções. Por exemplo, se quisermos trêsagrupamentos,podemossimplesmentedesfazerasduasúltimasjunções.

Nósusaremosumarepresentaçãomuitosimplesdeagrupamento.Nossosvaloresestarãoemagrupamentosfolha,querepresentaremoscomotuplasde1:

leaf1=([10,20],)#parafazerumatuplade1vocêprecisadevírgulasleaf2=([30,-15],)#senãoPythoninterpretaosparêntesescomoparênteses

Usaremos estes para criar agrupamentos fundidos, os quais representaremoscomotuplasde2(ordemdejunção,filhos):

merged=(1,[leaf1,leaf2])

Falaremosdaordemdejunçãodaquiapouco,mas,enquantoisso,vamoscriaralgumasfunçõesauxiliares:

defis_leaf(cluster):"""umagrupamentoéumafolhasetivertamanho1"""returnlen(cluster)==1

defget_children(cluster):"""retornaosdoisfilhosdesseagrupamentoseforumagrupamentofundido;criaumaexceçãoseforumagrupamentofolha"""ifis_leaf(cluster):

raiseTypeError("umagrupamentofolhanãotemfilhos")else:

returncluster[1]

defget_values(cluster):"""retornaovalornesteagrupamento(seforumagrupamentofolha)outodososvaloresnosagrupamentosfolhaabaixodele(senãofor)"""ifis_leaf(cluster):

Page 333: Data Science do zero: Primeiras regras com o Python

returncluster#jáéumatuplade1contendovalorelse:

return[valueforchildinget_children(cluster)forvalueinget_values(child)]

Afimdefundirosagrupamentosmaispróximos,precisamosdealgumanoçãodedistânciaentreagrupamentos.Usaremosadistânciamínimaentreelementosdedoisagrupamentos,quefundeosdoisagrupamentosmaispróximos(mas,àsvezes,produzirágrandesagrupamentosemcadeiaquenãosãotãopróximos).Sequiséssemosajustardoisagrupamentosesféricos,usaríamosadistânciamáxima,pois ela funde dois agrupamentos que se encaixam na menor bola. Ambasescolhassãocomuns,assimcomoéadistânciamédia:

defcluster_distance(cluster1,cluster2,distance_agg=min):"""computatodasasdistânciasentrecluster1ecluster2eaplica_distance_agg_nalistaresultante"""returndistance_agg([distance(input1,input2)

forinput1inget_values(cluster1)forinput2inget_values(cluster2)])

Usaremos a ordem de junção para acompanhar a ordem que fizemos oagrupamento.Númerosmenoresrepresentarãojunçõestardias.Issosignificaquequandoquisermosdesfazerajunçãodeagrupamentos,ofazemosdamenorparaamaiorjunção.Comoagrupamentosfolhanuncaforamfundidos,iremosatribuirinfinito(inf)aeles:

defget_merge_order(cluster):ifis_leaf(cluster):

returnfloat('inf')else:

returncluster[0]#aordemdejunçãoéoprimeiroelementodetuplade2

Agoraestamosprontosparacriaroalgoritmodeagrupamentos:defbottom_up_cluster(inputs,distance_agg=min):

#começacomcadaentradacomoumagrupamentofolha/tuplade1clusters=[(input,)forinputininputs]#enquantotivermosmaisdeumagrupamentofolharestante…whilelen(clusters)>1:

#encontraosdoisagrupamentosmaispróximosc1,c2=min([(cluster1,cluster2)

fori,cluster1inenumerate(clusters)forcluster2inclusters[:i]],key=lambda(x,y):cluster_distance(x,y,distance_agg))

Page 334: Data Science do zero: Primeiras regras com o Python

•••

#remove-osdalistadeagrupamentosclusters=[cforcinclustersifc!=c1andc!=c2]

#fazajunçãodeles,usandoaordemdejunção=númerosdeagrupamentosrestantesmerged_cluster=(len(clusters),[c1,c2])

#eadicionaajunçãodelesclusters.append(merged_cluster)

#quandosobrarapenasumagrupamento,retorne-oreturnclusters[0]

Seuusoébemsimples:base_cluster=bottom_up_cluster(inputs)

Issoproduzumagrupamentocujarepresentaçãoestranhaé:

Para cada agrupamento fundido, eu alinhei seus filhos verticalmente. Sedissermos“agrupamento0”paraoagrupamentocomordemde junção0,vocêpodeinterpretarcomo:

Agrupamento0éajunçãodoagrupamento1edoagrupamento2.Agrupamento1éajunçãodoagrupamento3edoagrupamento16.Agrupamento16éajunçãodafolha[11,15]edafolha[13,13].

Eassimpordiante…

Como tínhamos20entradas, foramnecessárias19 junçõesparaconseguiresse

Page 335: Data Science do zero: Primeiras regras com o Python

agrupamento.Aprimeirajunçãocriouoagrupamento18combinandoasfolhas[19,28]e[21,27].Eaúltimajunçãocriouoagrupamento0.

Noentanto,geralmentenãoqueremosrepresentaçõesruinscomoessa.(Mesmoqueessepossaserumexercíciointeressanteparacriarvisualizaçõesamigáveisaousuáriodehierarquiade agrupamento.)Emvezdisso, vamos escrever umafunção que gera qualquer número de agrupamentos desfazendo o númeroapropriadodejunções:

defgenerate_clusters(base_cluster,num_clusters):#comececomumalistaapenascomoagrupamentobaseclusters=[base_cluster]

#desdequeaindanãotenhamosagrupamentososuficiente…whilelen(clusters)<num_clusters:

#escolhaoquefoifundidoporúltimonext_cluster=min(clusters,key=get_merge_order)#remova-odalistaclusters=[cforcinclustersifc!=next_cluster]#eadicioneseusfilhosàlista,istoé,desfaçaafunçãoclusters.extend(get_children(next_cluster))

#umavezquetemosagrupamentososuficiente…returnclusters

Então,porexemplo,sequeremosgerartrêsagrupamentos,sóprecisamosfazer:three_clusters=[get_values(cluster)

forclusteringenerate_clusters(base_cluster,3)]

quepodemosrepresentarfacilmente:fori,cluster,marker,colorinzip([1,2,3],

three_clusters,['D','o','*'],['r','g','b']):

xs,ys=zip(*cluster)#truquemágicodedescompactarplt.scatter(xs,ys,color=color,marker=marker)

#colocaumnúmeronopontomédiodoagrupamentox,y=vector_mean(cluster)plt.plot(x,y,marker='$'+str(i)+'$',color='black')

plt.title("LocalizaçõesdeUsuários–3AgrupamentosBottom-up,Min")plt.xlabel("quadrasaolestedocentrodacidade")plt.ylabel("quadrasaonortedocentrodacidade")plt.show()

Issodáresultadosmuitodiferentesdosqueak-means,comoexibidonaFigura

Page 336: Data Science do zero: Primeiras regras com o Python

19-6.

Figura19-6.Trêsagrupamentosbottom-upusandodistânciamínima

Comomencionamosanteriormente, isso sedáporqueusarmin em cluster_distancetende a criar agrupamentos em cadeia. Se usarmos max parece (nos forneceagrupamentospróximos)igualaoresultadodemédia3(Figura19-7).

Aimplementaçãobottom_up_clusteringacimaérelativamentesimples,mastambéméchocantemente ineficiente.Ela computaadistância entre cadapardeentradaemcada passo.Uma implementaçãomais eficiente poderia pré-computar as distânciasentrecadapardeentradaseentãodarumaolhadadentrodecluster_distance.Umaimplementaçãorealmenteeficiente tambémlembrariadecluster_distancesdopassoanterior.

Page 337: Data Science do zero: Primeiras regras com o Python

Figura19-7.TrêsAgrupamentosbottom-upusandodistânciamáxima

Page 338: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosscikit-learnpossuiummódulocompletosklearn.cluster(http://scikit-learn.org/stable/modules/clustering.html)quecontémváriosalgoritmosdeagrupamentoincluindoKMeanseoalgoritmohierárquicoWarddeagrupamento(queusaumcritériodiferenteparaunirosagrupamentosdoqueosnossosmódulos).SciPy(http://www.scipy.org/)possuidoismodelosdeagrupamentosscipy.cluster.vq(quefazak-means)escipy.cluster.hierarchy(quepossuiumavariedadedealgoritmosdeagrupamentohierárquicos).

Page 339: Data Science do zero: Primeiras regras com o Python

CAPÍTULO20

ProcessamentodeLinguagemNatural

Elesforamaumgrandebanquetedelinguagenseroubaramassobras.—WilliamShakespeare

ProcessamentodeLinguagemNatural (Natural Language Processing—NLP)refere-seatécnicascomputacionaisenvolvendolinguagem.Éumcampoamplo,masveremosalgumastécnicassimpleseoutrasnão.

Page 340: Data Science do zero: Primeiras regras com o Python

NuvensdePalavrasNoCapítulo1,nóscomputamoscontagemdepalavrasdeinteressedeusuários.Uma técnica para visualizar e contar palavras é nuvem de palavras, que éartisticamente desenhar as palavras com tamanhos proporcionais às suascontagens.

Nogeral, os cientistas de dados nãopenammuito emnuvens de palavras, emgrandeparteporqueacolocaçãodaspalavrasnãosignificanadaalémde“esteéumespaçoondeeuconseguiencaixarumapalavra”.

Sevocêforforçadoacriarumanuvemdepalavras,pensesequerfazeroseixostransmitirem alguma coisa. Por exemplo, imagine que para cada coleção dedadosdejargõesrelacionadosàciênciavocêtenhadoisnúmerosentre0e100— o primeiro representando a frequência que ele aparece em postagens deempregoseosegundoafrequênciaqueapareceemcurrículos:

data=[("bigdata",100,15),("Hadoop",95,25),("Python",75,50),("R",50,40),("machinelearning",80,20),("statistics",20,60),("datascience",60,70),("analytics",90,3),("teamplayer",85,85),("dynamic",2,90),("synergies",70,0),("actionableinsights",40,30),("thinkoutofthebox",45,10),("self-starter",30,50),("customerfocus",65,15),("thoughtleadership",35,35)]

Aabordagemnuvemdepalavraséapenasparaorganizaraspalavrasnapáginausandoumafontebonita(Figura20-1).

Page 341: Data Science do zero: Primeiras regras com o Python

Figura20-1.NuvemdeJargões

Isso parece legal mas não nos diz nada. Uma abordagem mais interessantepoderiaserdispersá-lasparaqueaposiçãohorizontalindicassepopularidadedepostagens e a vertical popularidade de currículos, o que produziria umavisualizaçãoquetransmitiriaalgunsinsights(Figura20-2):

deftext_size(total):"""iguala8seototalfor0,28seototalfor200"""return8+total/200*20

forword,job_popularity,resume_popularityindata:plt.text(job_popularity,resume_popularity,word,

ha='center',va='center',size=text_size(job_popularity+resume_popularity))

plt.xlabel("PopularidadeemPostagensdeEmpregos")plt.ylabel("PopularidadeemCurrículos")plt.axis([0,100,0,100])plt.xticks([])plt.yticks([])plt.show()

Page 342: Data Science do zero: Primeiras regras com o Python

Figura20-2.Umanuvemdepalavrasmaissignificativa(semenosatrativa)

Page 343: Data Science do zero: Primeiras regras com o Python

Modelosn-gramasA vice-presidente de Marketing de Pesquisa quer que você crie milhares depáginaswebsobredata scienceparaqueseusite sejaclassificadono topodosresultadosdepesquisaparaostermosrelacionados.(Vocêtentaexplicarqueosalgoritmosdosmecanismosdepesquisassãoespertosobastanteequeissonãofuncionará,maselaserecusaaescutar.)

Claro, ela não quer escrever milhares de páginas web, nem quer pagar umahordade“estrategistasdeconteúdo”parafazê-lo.Emvezdisso,elaperguntasevocêpode,dealgumaforma,gerarestaspáginas.Parafazerisso,precisaremosdealgumalinguagemdemodelagem.

Umaabordagemécomeçarcomumcorpodedocumentoseaprenderummodeloestatístico de linguagem.Nonosso caso, começaremos como ensaio deMikeLoukidess“Whatisdatascience?”(http://oreil.ly/1Cd6ykN)

ComonoCapítulo9,usaremosrequestseBeautifulSouppararecuperarosdados.Háalgunsproblemasnosquaisprecisamosprestaratenção.

O primeiro é que os apóstrofos no texto são na verdade o caractere Unicodeu”\u2019”. Criaremos uma função auxiliar para substituí-las por apóstrofosnormais.

deffix_unicode(text):returntext.replace(u"\u2019","'")

Osegundoproblemaéqueumavezqueconseguirmosotextodapáginadaweb,vamos querer dividi-lo em uma sequência de palavras e pontos (para quepossamos dizer onde as sentenças terminam). Podemos fazer isso usandore.findall():

frombs4importBeautifulSoupimportrequestsurl="http://radar.oreilly.com/2010/06/what-is-data-science.html"html=requests.get(url).textsoup=BeautifulSoup(html,'html5lib')

content=soup.find("div","entry-content")#encontraconteúdodeentradadivregex=r"[\w']+|[\.]"#combinaumapalavraouumponto

Page 344: Data Science do zero: Primeiras regras com o Python

document=[]

forparagraphincontent("p"):words=re.findall(regex,fix_unicode(paragraph.text))document.extend(words)

Nóscertamentepoderíamos(edeveríamos)limparumpoucomaisessesdados.Ainda há uma quantidade de texto extrínseco no documento (por exemplo, aprimeirapalavraé“section”),nósdividimosempontosnomeiodasentença(porexemplo, em “Web 2.0) e existem legendas úteis e listas espalhadas por todolado.Ditoisso,trabalharemoscomodocumentcomoeleestá.

Agora que nós temos o texto como uma sequência de palavras, nós podemosmodelaruma linguagemdaseguinte forma:dadaalgumapalavra inicial (como“book”) olhamos para todas as palavras seguintes nos documentos fonte (aqui“isn't”, “a”, “shows”, “demonstrates”, e “teaches”). Nós escolhemosaleatoriamenteumasdessaspara serapróximapalavrae repetimosoprocessoaté chegar noponto, que significa o final da sentença.Nós chamamos isso demodelo bigrama, por ser determinado completamente por sequências debigramas(paresdepalavra)nosdadosoriginais.

Mas e a palavra inicial? Nós podemos apenas escolher aleatoriamente daspalavras que seguem o ponto. Para começar, vamos pré-computar as possíveistransições de palavras. Lembre-se que zip para quando qualquer uma de suasentradas termina,paraque zip(document, document[1:])nosdêprecisamenteosparesdeelementosconsecutivosdodocumento:

bigrams=zip(document,document[1:])transitions=defaultdict(list)forprev,currentinbigrams:

transitions[prev].append(current)

Agoraestamosprontosparagerarsentenças:defgenerate_using_bigrams():

current="."#issosignificaqueapróximapalavracomeçaráumasentençaresult=[]

whileTrue:next_word_candidates=transitions[current]#bigramas(current,_)current=random.choice(next_word_candidates)#escolheumaleatoriamenteresult.append(current)#anexa-oaosresultadosifcurrent==".":return"".join(result)#se“.”terminamos

Page 345: Data Science do zero: Primeiras regras com o Python

As sentenças que produz são besteiras, mas são o tipo de besteira que vocêdeveriacolocarnoseuwebsiteseestátentandofazerparecercomdatascience.Porexemplo:

Você deve saber quais são você quer dados ordenar dados abastecer web amigo alguém emtópicosde tendênciacomoosdadosemHadoopédatasciencerequerumlivrodemonstrarporque visualizações são mas nós fazemos correlações massivas através muitos comerciais discorígidoemlinguagemPythonecriaformamaismanejávelfazendoconexõesentãousaeusaissopararesolverosdados.

—ModeloBigrama

Nóspodemos tornarassentençasmenosbobasolhandopara trigrams, triosdepalavrasconsecutivas.(Demodomaisgeral,vocêpodeolharparan-gramscomnpalavrasconsecutivas,mastrêsserãoobastanteparanós.)Agoraastransiçõesdependerãodasduaspalavrasanteriores:

trigrams=zip(document,document[1:],document[2:])trigram_transitions=defaultdict(list)starts=[]

forprev,current,nextintrigrams:

ifprev==".":#sea“palavra”anterioreraumpontostarts.append(current)#entãoestaéumapalavrainicial

trigram_transitions[(prev,current)].append(next)

Note que agora temos que acompanhar as palavras inicias separadamente.Podemosgerarsentençaspraticamentedamesmaforma:

defgenerate_using_trigrams():current=random.choice(starts)#escolhaumapalavrainicialaleatóriaprev="."#eaprecedacomum'.'result=[current]whileTrue:

next_word_candidates=trigram_transitions[(prev,current)]next_word=random.choice(next_word_candidates)

prev,current=current,next_wordresult.append(current)

ifcurrent==".":return"".join(result)

Issoproduzsentençasmelhorescomo:

Page 346: Data Science do zero: Primeiras regras com o Python

EmretrospectoMapReduceparececomumaepidemiaecasosejaelenosdánovosinsightsemcomoaeconomiafuncionaIssonãoéumaperguntanóspoderíamosatéterperguntadoaalgunsanoshouveinstrumentação.

—ModeloTrigrama

Claro,elasparecemmelhorporqueemcadapassooprocessodegeraçãopossuimenosescolhaseemmuitospassosapenasumaescolha.Issosignificaquevocêfrequentemente gera sentenças (ao menos frases longas) que foram vistasliteralmentenosdadosoriginais.Maisdadosajudariam;atéfuncionariamelhorsevocêcoletassen-gramsdeváriosartigossobredatascience.

Page 347: Data Science do zero: Primeiras regras com o Python

GramáticasUma abordagem diferente para modelar linguagem é com gramáticas, regraspara gerar sentenças aceitáveis. No ensino fundamental, você provavelmenteaprendeu sobre partes do discurso e como combiná-las. Por exemplo, se vocêtinha um professor de inglês muito ruim, você pode dizer que uma sentençanecessariamente consiste de um substantivo seguido de um verbo. Se, então,você tem uma lista de substantivos e verbos, você pode gerar sentenças deacordocomaregra.

Definiremosumagramáticaumpoucomaiscomplicada:grammar={

"_S":["_NP_VP"],"_NP":["_N","_A_NP_P_A_N"],"_VP":["_V","_V_NP"],"_N":["datascience","Python","regression"],"_A":["big","linear","logistic"],"_P":["about","near"],"_V":["learns","trains","tests","is"]

}

Euinventeiaconvençãodequenomesquecomecemcomsublinhadosreferem-searegrasqueprecisamdemaiorexplicação,equeoutrosnomessãoterminaisquenãoprecisamdemaisprocessamento.

Então,porexemplo,“_S”éaregrade“sentença”,queproduzumaregra“_NP”(“frasenominal”)seguidadeumaregra“_VP”(“fraseverbal”).

Aregradafraseverbalpodeproduziraregra“_V”(“verbo”)ouaregraverboseguidadaregrafrasenominal.

Note que a regra “_NP” contém ela mesma em uma de suas produções. Asgramáticas podem ser recursivas, o que permite que até gramáticas infinitascomoestageremsentençasinfinitamentediferentes.

Comogeramossentençasapartirdestagramática?Começaremoscomumalistacontendoaregrasentença[“_S”].Eentãoexpandiremosrepetidamentecadaregra

Page 348: Data Science do zero: Primeiras regras com o Python

substituindo-a por uma escolha aleatória de suas produções. Nós pararemosquandotivermosumalistacontendoapenasterminais.

Porexemplo,umatalprogressãopodeparecercom:['_S']['_NP','_VP']['_N','_VP']['Python','_VP']['Python','_V','_NP']['Python','trains','_NP']['Python','trains','_A','_NP','_P','_A','_N']['Python','trains','logistic','_NP','_P','_A','_N']['Python','trains','logistic','_N','_P','_A','_N']['Python','trains','logistic','datascience','_P','_A','_N']['Python','trains','logistic','datascience','about','_A','_N']['Python','trains','logistic','datascience','about','logistic','_N']['Python','trains','logistic','datascience','about','logistic','Python']

Comoimplementamosisso?Bom,paracomeçar,criaremosumasimplesfunçãoauxiliarparaidentificarterminais:

defis_terminal(token):returntoken[0]!="_"

Em seguida, precisamos escrever uma função para transformar uma lista desímbolosemumasentença.Procuraremospeloprimeiro símbolonão terminal.Se não conseguimos encontrar um, significa que completamos a sentença eterminamos.

Se encontrarmos um não terminal, escolhemos aleatoriamente uma de suasproduções. Se essa produção é um terminal (por exemplo: uma palavra), nóssimplesmente substituímos o símbolo por ela. Caso contrário, será umasequência de símbolos não terminais separados por espaço que precisamosseparar(split).eentãoencaixaremsímbolosatuais.Dequalquerforma,repetimosoprocessononovoconjuntodesímbolos.

Colocandotudojuntoconseguimos:defexpand(grammar,tokens):

fori,tokeninenumerate(tokens):

#pulaosterminaisifis_terminal(token):continue

Page 349: Data Science do zero: Primeiras regras com o Python

#sechegamosaqui,encontramosumsímbolonãoterminal#entãoprecisamosescolherumsubstitutoaleatórioreplacement=random.choice(grammar[token])

ifis_terminal(replacement):tokens[i]=replacement

else:tokens=tokens[:i]+replacement.split()+tokens[(i+1):]

#agorachamaexpanddanovalistadesímbolosreturnexpand(grammar,tokens)

#sechegamosaqui,temostodososterminaiseacabamosreturntokens

Agorapodemoscomeçaragerarsentenças:defgenerate_sentence(grammar):

returnexpand(grammar,["_S"])

Tente mudar a gramática — acrescente mais palavras, mais regras e suaspróprias partes do discurso— até que você esteja pronto para gerar tantaspáginaswebquantosuaempresaprecisa.

Asgramáticassãomaisinteressantesquandousadasemoutradireção.Dadaumasentençapodemosusarumagramáticaparaanalisarasentença.Issopermitequeidentifiquemossujeitoseverbosenosajudaaentenderasentença.

Usardata scienceparagerar textoéum truqueesperto;usá-laparaentenderotexto é mais mágico. (Veja “Para Mais Esclarecimentos” na página 200 asbibliotecasquevocêpoderiausar.)

Page 350: Data Science do zero: Primeiras regras com o Python

UmAdendo:AmostragemdeGibbsGerar amostras de algumas distribuições é fácil. Nós podemos conseguirvariáveisaleatóriasuniformescom:

random.random()

evariáveisaleatóriasnormaiscom:inverse_normal_cdf(random.random())

Masalgumasdistribuiçõessãomaisdifíceisdecriaramostras.AamostragemdeGibbs é uma técnica para gerar amostras de distribuições multidimensionaisquandoapenasconhecemosalgumasdasdistribuiçõescondicionais.

Por exemplo, imagine que está jogando dois dados. Deixe x ser o valor doprimeiro dado e y a soma dos dados, e imagine que você queria gerarmuitospares(x,y).Nestecasoéfácilgeraramostrasdiretamente:

defroll_a_die():returnrandom.choice([1,2,3,4,5,6])

defdirect_sample():d1=roll_a_die()d2=roll_a_die()returnd1,d1+d2

Masimaginequevocêsóconheciaasdistribuiçõescondicionais.Adistribuiçãode y condicionado a x é fácil — se você sabe o valor de x, y é igualmentepossíveldeserx+1,x+2,x+3,x+4,x+5oux+6:

defrandom_y_given_x(x):"""igualmentepossíveldeserx+1,x+2,…,x+6"""returnx+roll_a_die()

Aoutradireçãoémaiscomplicada.Porexemplo,sevocêsabequeyé2,entãonecessariamentexé1(poisaúnicaformadedoisdadossomarem2éseambosforem1).Sevocêsabequeyé3,entãoxé igualmentepossíveldeser1ou2.Similarmente,seyé11,entãoxtemqueser5ou6:

defrandom_x_given_y(y):ify<=7:

#seototalé7oumenos,oprimeirodadoéigualmente#possíveldeser1,2,...,(total–1)

Page 351: Data Science do zero: Primeiras regras com o Python

returnrandom.randrange(1,y)else:

#seototalé7oumais,oprimeirodadoéigualmente#possíveldeser(total–6),(total–5),…,6returnrandom.randrange(y-6,7)

A forma como a amostragem de Gibbs funciona é que se começamos comqualquervalor(válido)paraxeyeentãorepetidaealternadamentesubstituímosx por um valor aleatório escolhido condicionado a y e substituímos y por umvalor aleatório escolhido condicionado a x. Após um número de iterações, osvalores de x e y representarão uma amostra de uma distribuição conjuntaincondicional:

defgibbs_sample(num_iters=100):x,y=1,2#nãoimportafor_inrange(num_iters):

x=random_x_given_y(y)y=random_y_given_x(x)

returnx,y

Vocêpodeverificarqueissoforneceresultadosparecidosaosdaamostradireta:defcompare_distributions(num_samples=1000):

counts=defaultdict(lambda:[0,0])for_inrange(num_samples):

counts[gibbs_sample()][0]+=1counts[direct_sample()][1]+=1

returncounts

Usaremosessatécnicanapróximaseção.

Page 352: Data Science do zero: Primeiras regras com o Python

••

ModelagemdeTópicosQuandoconstruímosnossarecomendaçãoCientistasdeDadosqueVocêDeveriaConhecernoCapítulo1,nóssimplesmenteprocuramoscombinaçõesexatasdosinteressesdeclaradosdaspessoas.

Umaabordagemmais sofisticadapara entender comoos interessesdosnossosusuáriospodesertentaridentificarostópicosquesustentamessestópicos.Umatécnica chamada Análise Latente de Dirichlet (Latent Dirichlet's Analysis –LDA)écomumenteusadapara identificar tópicoscomunsemumacoleçãodedocumentos. Aplicaremos isso a documentos que consistem em interesses decadausuário.

A LDA possui algumas semelhanças com o Classificador Naive Bayes queconstruímos no Capítulo 13, no que assume um modelo probabilístico paradocumentos. Nós maquiaremos os detalhes matemáticos pesados, mas paranossosobjetivosomodelopresumeque:

HáumnúmerofixoKdetópicos.Existeumavariávelaleatóriaqueatribuiacadatópicoumaprobabilidadededistribuiçãoassociadaàspalavras.Vocêdeveriapensarnessadistribuiçãocomoaprobabilidadedeverapalavrawdadootópicok.Aindaháoutravariávelaleatóriaqueatribuiacadadocumentoaprobabilidadededistribuiçãodetópicos.Vocêdeveriapensarnessadistribuiçãocomoumamisturadetópicosnodocumentod.Cadapalavraemumdocumentofoigeradaprimeiropelaescolhaaleatóriadeumtópico(dadistribuiçãodetópicosdodocumento)eentãoumapalavra(dadistribuiçãodepalavrasdostópicos).

Nóstemosumacoleçãodedocumentsemquecadaumaéuma listdepalavras.Etemosumacoleçãocorrespondentede document_topics que atribuium tópico (umnúmeroentre0eK–1)acadapalavraemcadadocumento.

Paraqueaquintapalavranoquartodocumentoseja:documents[3][4]

Page 353: Data Science do zero: Primeiras regras com o Python

eotópicodeondeaquelapalavrafoiescolhida:document_topics[3][4]

Isso explicitamente define a distribuição de tópicos de cada documento eimplicitamentedefineadistribuiçãodepalavrasdecadatópico.

Nóspodemosestimaraprobabilidadedeotópico1produzirumacertapalavracomparando quantas vezes o tópico 1 produz qualquer palavra. Da mesmaforma, quando construímos um filtro para spam no Capítulo 13, comparamosquantasvezesumapalavraapareciaemspamscomonúmero totaldepalavrasqueaparecememspams.

Apesar de esses tópicos serem apenas números, podemos dar a eles nomesdescritivosolhandoparaaspalavrasnasquaiselescolocammaispeso.Nóssóprecisamosgerardealgumaformaodocument_topics.ÉaquiqueaamostragemdeGibbsentraemação.

Nós começamos atribuindo a cada palavra em cada documento um tópicocompletamentealeatório.Entãopassamosporcadadocumento,umapalavraporvez.Paraaquelapalavraedocumento,nósconstruímospesosparacada tópicoque dependem da distribuição (atual) de tópicos naquele documento e dadistribuição(atual)depalavrasparaaqueletópico.Então,usamostaispesosparaamostrarumnovotópicoparaaquelapalavra.Seiterarmosesseprocessomuitasvezes, acabaremos comumaamostra conjuntadadistribuição tópico-palavra edadistribuiçãodocumento-tópico.

Para começar, precisaremos de uma função para escolher aleatoriamente umíndicebaseadonoconjuntoarbitráriodepesos:

defsample_from(weights):"""retornaicomprobabilidadedeweights[i]/sum(weights)"""total=sum(weights)rnd=total*random.random()#uniformeentre0etotalfori,winenumerate(weights):

rnd-=w#retornaomenoritalqueifrnd<=0:returni#weights[0]+…+weights[i]>=rnd

Porexemplo,sevocêdápesos[1,1,3]entãoumquintodotempoeleretornará0,umquintodotempoeleretornará1etrêsquintosdotemporetornará2.

Page 354: Data Science do zero: Primeiras regras com o Python

Nossosdocumentossãoosinteressesdenossosusuários,queparecemcom:documents=[

["Hadoop","BigData","HBase","Java","Spark","Storm","Cassandra"],["NoSQL","MongoDB","Cassandra","HBase","Postgres"],["Python","scikit-learn","scipy","numpy","statsmodels","pandas"],["R","Python","statistics","regression","probability"],["machinelearning","regression","decisiontrees","libsvm"],["Python","R","Java","C++","Haskell","programminglanguages"],["statistics","probability","mathematics","theory"],["machinelearning","scikit-learn","Mahout","neuralnetworks"],["neuralnetworks","deeplearning","BigData","artificialintelligence"],["Hadoop","Java","MapReduce","BigData"],["statistics","R","statsmodels"],["C++","deeplearning","artificialintelligence","probability"],["pandas","R","Python"],["databases","HBase","Postgres","MySQL","MongoDB"],["libsvm","regression","supportvectormachines"]

]

EtentaremosencontrarK=4tópicos.

Para calcular os pesos da amostragem, precisaremos acompanhar váriascontagens.Primeirovamoscriarasestruturasdedadosparaelas.

Quantasvezescadatópicoéatribuídoacadadocumento:#umalistadeContadores,umaparacadadocumentodocument_topic_counts=[Counter()for_indocuments]

Quantasvezescadapalavraéatribuídaparacadatópico:#umalistadeContadores,umaparacadatópicotopic_word_counts=[Counter()for_inrange(K)]

Onúmerototaldepalavrasatribuídasacadatópico:#umalistadenúmeros,umaparacadatópicotopic_counts=[0for_inrange(K)]

Onúmerototaldepalavrascontidasemcadadocumento:#umalistadenúmeros,umaparacadadocumentodocument_lengths=map(len,documents)

Onúmerodepalavrasdistintas:distinct_words=set(wordfordocumentindocumentsforwordindocument)

Page 355: Data Science do zero: Primeiras regras com o Python

W=len(distinct_words)

Eonúmerodedocumentos:D=len(documents)

Por exemplo, uma vez que populamos estes, podemos encontrar o número depalavrasemdocuments[3]associadocomotópico1como:

document_topic_counts[3][1]

E podemos encontrar o número de vezes quenlp é associado com o tópico 2como:

topic_word_counts[2]["nlp"]

Agoraestamosprontosparadefinirnossasfunçõesdeprobabilidadecondicional.ComonoCapítulo13, cada umapossui um termo suavizador que garante quetodotópicopossuaumachancediferentedezerodeserescolhidoemqualquerdocumento e que cada palavra possua uma chance diferente de zero de serescolhidaemqualquertópico:

defp_topic_given_document(topic,d,alpha=0.1):"""afraçãodepalavrasnodocumento_d_quesãoatribuídasa_topic_(maisalgumacoisa)"""

return((document_topic_counts[d][topic]+alpha)/(document_lengths[d]+K*alpha))

defp_word_given_topic(word,topic,beta=0.1):"""afraçãodepalavrasatribuídasa_topic_queéiguala_word_(maisalgumacoisa)"""

return((topic_word_counts[topic][word]+beta)/(topic_counts[topic]+W*beta))

Usaremosestesparacriarpesosparaatualizarostópicos:deftopic_weight(d,word,k):

"""dadoumdocumentoeumapalavranaqueledocumento,retorneopesoparaok-ésimotópico"""returnp_word_given_topic(word,k)*p_topic_given_document(k,d)

defchoose_new_topic(d,word):returnsample_from([topic_weight(d,word,k)

forkinrange(K)])

Page 356: Data Science do zero: Primeiras regras com o Python

Existem sólidos motivos matemáticos para o porquê de topic_weight ter sidodefinido de tal forma, mas seus detalhes nos levariammuito fora do escopo.Felizmente, faz sentido que — dada uma palavra e seu documento — aprobabilidadede escolhadequalquer tópicodependadequãoprovável aqueletópicoéparaodocumentoequãoprovávelaquelapalavraéparaotópico.

Isso é todo o maquinário do qual precisamos. Começamos atribuindo cadapalavraaumtópicoaleatórioepopulandonossoscontadoresapropriadamente:

random.seed(0)document_topics=[[random.randrange(K)forwordindocument]

fordocumentindocuments]

fordinrange(D):forword,topicinzip(documents[d],document_topics[d]):

document_topic_counts[d][topic]+=1topic_word_counts[topic][word]+=1topic_counts[topic]+=1

Nosso objetivo é conseguir uma amostra auxiliar da distribuição tópicos-palavras e da distribuição documentos-tópicos. Nós fazemos isso usando umaforma de amostragem deGibbs que usa probabilidades condicionais definidaspreviamente:

foriterinrange(1000):fordinrange(D):

fori,(word,topic)inenumerate(zip(documents[d],document_topics[d])):

#removaestapalavra/tópicodacontagem#paraquenãoinfluencienospesosdocument_topic_counts[d][topic]-=1topic_word_counts[topic][word]-=1topic_counts[topic]-=1document_lengths[d]-=1

#escolhaumnovotópicobaseadonospesosnew_topic=choose_new_topic(d,word)document_topics[d][i]=new_topic

#aagoracoloque-odevoltanascontasdocument_topic_counts[d][new_topic]+=1topic_word_counts[new_topic][word]+=1topic_counts[new_topic]+=1document_lengths[d]+=1

Oquesãoostópicos?Elessãoapenasnúmeros0,1,2,e3.Sequisermosnomes

Page 357: Data Science do zero: Primeiras regras com o Python

paraelestemosquefazerissonósmesmos.Vamosveraspalavrascommaiorespesosparacadatópico(Tabela20-1):

fork,word_countsinenumerate(topic_word_counts):forword,countinword_counts.most_common():

ifcount>0:printk,word,count

Tabela20-1.Palavrasmaiscomunsportópico

Baseadonissoeuprovavelmenteatribuirianomesdetópicos:topic_names=["BigDataelinguagensdeprogramação",

"Pythoneestatística","basesdedados","aprendizadodemáquina"]

agora podemos ver como o modelo atribui tópicos aos interesses de cadausuário:

fordocument,topic_countsinzip(documents,document_topic_counts):printdocumentfortopic,countintopic_counts.most_common():

ifcount>0:printtopic_names[topic],count,

print

quenosdá:['Hadoop','BigData','HBase','Java','Spark','Storm','Cassandra']BigDataelinguagemdeprogramação4basesdedados3['NoSQL','MongoDB','Cassandra','HBase','Postgres']basesdedados5['Python','scikit-learn','scipy','numpy','statsmodels','pandas']Pythoneestatística5aprendizadodemáquina1

eassimpordiante.Dadosos“e”queprecisamosemalgunsdenossosnomesdetópicos, é possível que usemos mais tópicos, embora seja provável que não

Page 358: Data Science do zero: Primeiras regras com o Python

tenhamosdadososuficienteparaaprendê-loscomsucesso.

Page 359: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosNaturalLanguageToolkit(http://www.nltk.org/)éumabibliotecapopulardeferramentasNLPparaPython.Elapossuiseuprópriolivrocompleto(http://www.nltk.org/book/),queestádisponívelparaleituraonline.gensim(http://radimrehurek.com/gensim/)éumabibliotecaPythonparamodelagemdetópicos,queéumaapostamelhordoquenossomodeloapartirdozero.

Page 360: Data Science do zero: Primeiras regras com o Python

CAPÍTULO21

AnálisedeRede

Suasconexõesatodasascoisasaoseuredor,literalmente,definemquemvocêé.—AaronO'Connell

Muitos problemas de dados interessantes podem ser lucrativos pensando emtermosderedes,consistentesdenósdealgumtipoevínculosqueasjuntam.

Por exemplos, seus amigos do Facebook formam os nós de uma rede cujosvínculossãorelaçõesdeamizade.UmexemplomenosóbvioéaprópriaWorldWideWeb,comcadapáginasendoumnóecadahiperlinkdeumapáginaparaoutraumvínculo.

Amizade de Facebook é mútua — se eu sou seu amigo no Facebook, vocênecessariamente é meu amigo. Nesse caso, dizemos que os vínculos são nãodirecionados. Hiperlinks não são — meu website possui links parawhitehouse.gov,mas (por razões inexplicáveis)whitehouse.gov se recusa a terlinkparaomeuwebsite.Nóschamamosessestiposdevínculosdedirecionados.Veremososdoistiposderedes.

Page 361: Data Science do zero: Primeiras regras com o Python

CentralidadedeIntermediaçãoNoCapítulo 1, nós computamos os conectores chave na redeDataSciencestercontando o número de amigos que cada usuário tinha. Agora nós temosmaquinário o suficiente para ver outras abordagens. Lembre-se de que a rede(Figura21-1)englobavausuários:

users=[{"id":0,"name":"Hero"},{"id":1,"name":"Dunn"},{"id":2,"name":"Sue"},{"id":3,"name":"Chi"},{"id":4,"name":"Thor"},{"id":5,"name":"Clive"},{"id":6,"name":"Hicks"},{"id":7,"name":"Devin"},{"id":8,"name":"Kate"},{"id":9,"name":"Klein"}

]

eamizades:friendships=[(0,1),(0,2),(1,2),(1,3),(2,3),(3,4),

(4,5),(5,6),(5,7),(6,8),(7,8),(8,9)]

Figura21-1.AredeDataSciencester

Nóstambémadicionamoslistasdeamigosparacadadictdeusuário:foruserinusers:

user["friends"]=[]

fori,jinfriendships:

Page 362: Data Science do zero: Primeiras regras com o Python

1.

2.

3.

#issofuncionaporqueusers[i]éousuáriocujaidéiusers[i]["friends"].append(users[j])#adicionaicomoamigodejusers[j]["friends"].append(users[i])#adicionajcomoamigodei

Quando começamos, estávamos insatisfeitos com nossa noção de grau decentralidade, que não concordava com nossa intuição sobre quem eram osconectores-chavedarede.

Umaalternativamétricaécentralidadedeintermediação,queidentificapessoasque frequentemente estão nomenor caminho entre pares de outras pessoas.Acentralidadedeintermediaçãodenóiécomputadaadicionandoparacadaoutropardenósjek,aproporçãodoscaminhosmaiscurtosentreonójeonókquepassapori.

Isto é, para entender a centralidade de intermediação de Thor, precisamoscomputartodososcaminhosmaiscurtosentretodososparesdepessoasquenãosão Thor. E então precisaremos contar quantos desses caminhos mais curtospassamporThor. Por exemplo, o único caminhomais curto entreChi (id 3) eClive (id 5) passa por Thor, enquanto nenhum dos dois caminhos mais curtosentreHero(id0)eChi(id3)passa.

Portanto,comoprimeiropasso,precisaremosdescobriroscaminhosmaiscurtosentretodososparesdepessoas.Existemalgunsalgoritmosbemsofisticadosparafazer isso eficientemente, mas (como quase sempre acontece) usaremos ummenoseficienteemaisfácildeentender.

Este algoritmo (uma implementação de busca em largura) é um dos maiscomplicadosnestelivro,entãofalaremoscuidadosamentesobreele:

Nosso objetivo é uma função que pega from_user e encontra todos oscaminhosmaiscurtosparatodososoutrosusuários.Representaremos um caminho como uma list de IDs de usuários. Comotodo caminho começa em from_user, não incluiremos seu ID na lista. Issosignifica queo tamanhoda lista representandoo caminho será o própriotamanhodocaminho.Manteremos um dicionário shortest_paths_to onde as chaves são IDs deusuárioseosvaloressãolistasdecaminhosqueterminamnousuáriocomo ID especificado. Se existe um único menor caminho, a lista conterá

Page 363: Data Science do zero: Primeiras regras com o Python

4.

5.

6.

7.

8.

apenas aquele caminho. Se existemmúltiplos caminhosmenores, a listaconterátodoseles.Tambémmanteremosuma fila frontier que contém usuários que queremosexplorar na ordem emque queremos explorá-los.Os armazenaremos empares (prev_user, user) para que saibamos como chegamos a cada um deles.Inicializaremos a lista com todos os vizinhos de from_user. (Ainda nãofalamos sobre filas, as quais são estruturas otimizadas de operações“adicione ao final” e “remova do começo”. Em Python, elas sãoimplementadascomocollections.dequequenaverdadeéumafiladuplamenteterminada.)À medida que exploramos o gráfico, sempre que encontramos novosvizinhosparaosquaisaindanãoconhecemososcaminhosmaiscurtos,nósosadicionamosaofinaldafilaparaexplorarmosmaistarde,comousuárioatualprev_user.Quandotiramosumusuáriodafilaquenuncaencontramosantes,nós,comcerteza,encontramosumoumaiscaminhosmaiscurtosparaele—cadamenorcaminhoparaprev_usercomumpassoextra.Quando tiramos um usuário da fila que já havíamos encontrado aqueleusuárioantes,entãoouencontramosoutromenorcaminho(em,cujocaso,deveremos adicionar o usuário) ou encontramosumcaminhomaior (em,cujocaso,nãodeveríamosadicionarousuário).Quando não hámais usuários na fila, exploramos todo o gráfico (ou, aomenos, as partes possíveis de serem analisadas a partir do usuário comqueminiciamos)eterminamos.

Podemoscolocartudojuntoemuma(grande)função:fromcollectionsimportdeque

defshortest_paths_from(from_user):

#umdicionáriopara“user_id”para*todos*oscaminhos#maiscurtosparaaqueleusuárioshortest_paths_to={from_user["id"]:[[]]}

#umafilade(previoususer,nextuser)queprecisamosverificar.#começacomtodosospares(from_user,friend_of_from_user)

frontier=deque((from_user,friend)forfriendinfrom_user["friends"])

Page 364: Data Science do zero: Primeiras regras com o Python

#continueatéesvaziarafilawhilefrontier:

prev_user,user=frontier.popleft()#removaousuárioqueéuser_id=user["id"]#oprimeironafila

#pelamaneiracomoestamosadicionandonafila,#necessariamentejáconhecemosalgunsdoscaminhosmaiscurtosparaprev_userpaths_to_prev_user=shortest_paths_to[prev_user["id"]]new_paths_to_user=[path+[user_id]forpathinpaths_to_prev_user]

#épossívelquejásaibamosummenorcaminhoold_paths_to_user=shortest_paths_to.get(user_id,[])

#qualéomenorcaminhoatéaquiquejávimosatéagora?ifold_paths_to_user:

min_path_length=len(old_paths_to_user[0])else:

min_path_length=float('inf')

#apenasmantémcaminhosquenãosãolongosdemaisesãonovosnew_paths_to_user=[path

forpathinnew_paths_to_useriflen(path)<=min_path_lengthandpathnotinold_paths_to_user]

shortest_paths_to[user_id]=old_paths_to_user+new_paths_to_user

#addnever-seenneighborstothefrontierfrontier.extend((user,friend)

forfriendinuser["friends"]iffriend["id"]notinshortest_paths_to)

returnshortest_paths_to

Agorapodemosarmazenaressesdictscomcadanó:foruserinusers:

user["shortest_paths"]=shortest_paths_from(user)

E finalmente estamos prontos para computar a centralidade de intermediação.Para todo par de nós i e j, conhecemos osn caminhosmais curtos para i e j.Então,paracadaumdessescaminhos,apenasadicionamos1/nàcentralidadedecadanónaquelecaminho:

foruserinusers:user["betweenness_centrality"]=0.0

forsourceinusers:source_id=source["id"]fortarget_id,pathsinsource["shortest_paths"].iteritems():

ifsource_id<target_id:#nãocontaduasvezesnum_paths=len(paths)#quantoscaminhosmaiscurtos?

Page 365: Data Science do zero: Primeiras regras com o Python

contrib=1/num_paths#contribuiçãoparacentralidadeforpathinpaths:

foridinpath:ifidnotin[source_id,target_id]:users[id]["betweenness_centrality"]+=contrib

Figura 21-2. A rede DataSciencester dimensionada por centralidade deintermediação

Como exibido na Figura 21-2, os usuários 0 e 9 têm centralidade 0 (porquenenhumdelesestáemnenhumcaminhomenorentreoutrosusuários),mas3,4e5 têm altas centralidades (porque todos os três estão em muitos caminhosmenores).

Geralmente, os números de centralidade não são significativos. O que importa écomoosnúmerosparacadanósecomparamcomosnúmerosdosoutrosnós.

Outramedidaquepodemosver écentralidadedeproximidade. Primeiro, paracadausuário,nóscomputamossuadistância,queéasomadostamanhosdeseuscaminhos mais curtos para cada outro usuário. Como já computamos oscaminhosmaiscurtosentrecadapardenós,éfácilcalcularseustamanhos.(Sehouvermúltiploscaminhoscurtos,todoselespossuemomesmotamanho,logopodemosanalisaroprimeiro.)

deffarness(user):"""asomadostamanhosdoscaminhosmaiscurtosparacadaoutrousuário"""returnsum(len(paths[0])

forpathsinuser["shortest_paths"].values())

Page 366: Data Science do zero: Primeiras regras com o Python

após isso temos pouquíssimo trabalho para computar a centralidade deproximidade(Figura21-3):

foruserinusers:user["closeness_centrality"]=1/farness(user)

Figura 21-3. A rede DataSciencester dimensionada por centralidade deproximidade

Hábemmenosvariaçãoaqui—mesmoosnóscentraisaindaestãobemlongedosnósnosarredores.

Como vimos, computar os caminhosmais curtos dá uma certa chateação. Porisso, centralidades de intermediação e de proximidade não são usadas comfrequênciaemgrandesredes.Acentralidademenosintuitiva(masmaisfácildecomputar),centralidadedevetorpróprioéusadacommaisfrequência.

Page 367: Data Science do zero: Primeiras regras com o Python

CentralidadedeVetorPróprioPara falar sobre centralidade de vetor próprio, temos que falar sobre vetorespróprios,eparafalarsobrevetorespróprios,temosquefalarsobremultiplicaçãodematrizes.

MultiplicaçãodeMatrizesSeAéumamatrizn1xk1eBéumamatrizn2×k2,esek1=n2entãooprodutoABdeleséamatrizn1×k2cujaentrada(i,j)é:

Que é apenasoprodutodot da i-ésima linhadeA coma j-ésima colunadeB(tambémchamadosdevetores):

defmatrix_product_entry(A,B,i,j):returndot(get_row(A,i),get_column(B,j))

depoisdoquê,temos:defmatrix_multiply(A,B):

n1,k1=shape(A)n2,k2=shape(B)ifk1!=n2:raiseArithmeticError("formatosincompatíveis!")

returnmake_matrix(n1,k2,partial(matrix_product_entry,A,B))

Noteque,seAéumamatrizn×keBéumamatrizk×1,entãoABéumamatrizn×1.Setratamosumvetorcomoumamatrizdeumacoluna,podemospensarem A como uma função que mapeia vetores dimensionais k para vetoresdimensionaisn,emqueafunçãoéapenasamultiplicaçãodamatriz.

Anteriormente,representamosvetoresapenascomolistas,masnãosãoamesmacoisa:

v=[1,2,3]v_as_matrix=[[1],

[2],[3]]

Então precisaremos de algumas funções auxiliares para converter de um lado

Page 368: Data Science do zero: Primeiras regras com o Python

paraooutroasduasrepresentações:defvector_as_matrix(v):

"""retornaovetorv(representadocomoumalista)comoumamatrizn×1"""return[[v_i]forv_iinv]

defvector_from_matrix(v_as_matrix):"""retornaamatriznx1comoumalistadevalores"""return[row[0]forrowinv_as_matrix]

eapóspodemosdefiniraoperaçãodematrizusandomatrix_multiply:defmatrix_operate(A,v):

v_as_matrix=vector_as_matrix(v)product=matrix_multiply(A,v_as_matrix)returnvector_from_matrix(product)

QuandoAéumamatrizquadrada,estaoperaçãomapeiavetoresdimensionaisnparaoutrosvetoresdimensionaisn.Épossívelque,paraalgumamatrizAevetorv,quandoAoperaemvconseguimosummúltiplodev.Issoé,oresultadoéumvetorqueapontaparaamesmadireçãoquev.Quandoissoacontece(equando,além disso, v não é um vetor de zeros), nós chamamos v de vetor próprio(autovetor)deA.Echamamosomultiplicadordevalorpróprio(autovalor).

Uma possívelmaneira de encontrar o vetor próprio deA é escolher um vetorinicialv,aplicarmatrix_operate,reescalaroresultadoparatermagnitude1erepetiratéoprocessoconvergir:

deffind_eigenvector(A,tolerance=0.00001):guess=[random.random()for__inA]

whileTrue:result=matrix_operate(A,guess)length=magnitude(result)next_guess=scalar_multiply(1/length,result)

ifdistance(guess,next_guess)<tolerance:returnnext_guess,length#vetorpróprio,valorpróprioguess=next_guess

Por construção, o guess retornado é um vetor tal que, quando você aplicamatrix_operateaeleeoreescalaparatertamanho1,vocêrecebedevolta(umvetormuitopróximoa)elemesmo.Oquesignificaqueeleéumvetorpróprio.

Nemtodasasmatrizesdenúmerosreaispossuemvetoresevalorespróprios.Porexemplo,amatriz:

Page 369: Data Science do zero: Primeiras regras com o Python

rotate=[[0,1],[-1,0]]

giravetoresa90grausnosentidohorário,oquesignificaqueoúnicovetorqueelamapeia para ummúltiplo em escala de simesmo é umvetor de zeros. Sevocê tentou find_eigenvector(rotate) ele poderia rodar para sempre. Até mesmomatrizes com vetores próprios podem, às vezes, ficar presas em ciclos.Considereamatriz:

flip=[[0,1],[1,0]]

Essa matriz mapeia qualquer vetor [x, y] para [y, x]. Isso significa que, porexemplo, [1, 1] é um vetor próprio com valor próprio 1. Entretanto, se vocêcomeça com um vetor aleatório com coordenadas deferentes, find_eigenvector irárepetidamente trocar as coordenadas para sempre. Bibliotecas que não são dozero como NumPy usam métodos diferentes que funcionariam nesse caso.Todavia,quandofind_eigenvectorretornaumresultado,talresultadoésemdúvidasumvetorpróprio.

CentralidadeComoissonosajudaaentenderaredeDataSciencester?

Para começar, precisaremos representar as conexões em nossa rede comoadjacency_matrix,cujaentrada(i,j)thoué1(seusuáriosiejforemamigos)ou0(senãoforem):

defentry_fn(i,j):return1if(i,j)infriendshipsor(j,i)infriendshipselse0

n=len(users)adjacency_matrix=make_matrix(n,n,entry_fn)

A centralidade de vetor próprio para cada usuário é a entrada correspondenteàqueleusuárionovetorpróprioretornadoporfind_eigenvector(Figura21-4):

Pormotivostécnicosqueestãoalémdoescopodestelivro,qualquermatrizadjacentediferentedezeronecessariamentepossuiumvetorpróprioparatodasaquelascujosvaloresnãosãonegativos.Efelizmenteparanós,paraessaadjacency_matrix nossafunçãofind_eigenvectoroencontra.

Page 370: Data Science do zero: Primeiras regras com o Python

eigenvector_centralities,_=find_eigenvector(adjacency_matrix)

Figura 21-4. A rede DataSciencester dimensionada por centralidade de vetorpróprio

Usuários comalta centralidadedevetor própriodeveriam ser aquelesque têmmuitasconexõeseconexõescompessoasquetêmaltacentralidade.

Aquiosusuários1e2sãoosmaiscentrais,poisambospossuemtrêsconexõescom pessoas que são altamente centrais. Conforme nos afastamos deles, ascentralidadesdaspessoasgradualmentediminuem.

Em uma rede pequena assim, a centralidade do vetor próprio comporta-se deforma instável. Se você tentar adicionar ou remover links, descobrirá quepequenas modificações na rede podemmudar dramaticamente os números decentralidade.Emumaredemaioressenãoseriaocaso.

Nós ainda não chegamos no porquê de um vetor próprio poder levar a umanoção razoável de centralidade. Ser um vetor próprio significa que se vocêcomputa:

matrix_operate(adjacency_matrix,eigenvector_centralities)

oresultadoéummúltiploescalardeeigen_vector_centralities.

Sevocêolharcomoamultiplicaçãodematrizfunciona,matrix_operateproduzumvetorcujoi-ésimoelementoé:

dot(get_row(adjacency_matrix,i),eigenvector_centralities)

queéprecisamenteasomadascentralidadesdevetorprópriodeoutrosusuários

Page 371: Data Science do zero: Primeiras regras com o Python

1.

2.

conectadosaousuárioi.

Em outras palavras, as centralidades de vetor próprio são números, um porusuário, tal que cada valor de usuário é um múltiplo constante da soma dosvalores de seus vizinhos. Neste caso, centralidade significa estar conectado apessoas que são elas mesmas centrais. Quanto mais você estiver diretamenteconectado a nós com centralidade, mais central você é. Isso é uma definiçãocircular—vetoresprópriossãoumaformadesairdocírculo.

Outramaneiradeentenderissoépensarnoquêfind_eigenvectorestáfazendoaqui.Elecomeçaatribuindocadanóaumacentralidadealeatória.Entãorepeteosdoispassosaseguiratéoprocessoconvergir:

Dáacadanóumnovovalordecentralidadequeéigualasomadosvaloresdascentralidades(antigas)deseusvizinhos.Reescalaovetordecentralidadesparatermagnitude1.

Emboraamatemáticaportrásdissopareçaumtantoopacanoinício,ocálculoérelativamentedireto(diferentedecentralidadeporintermediação)eémuitofácildefazeremgráficosmuitograndes.

Page 372: Data Science do zero: Primeiras regras com o Python

GráficosDirecionadosePageRankDataSciencester não está conseguindo deslanchar, então a vice-presidente deRendimentos considera trocar de ummodelo de amizade para ummodelo deaprovação.Acontecequeninguémparticularmenteseimportaquaiscientistasdedadossãoamigosumdooutro,masrecrutadoresseimportammuitocomquaiscientistasdedadossãorespeitadosporoutroscientistas.

Nesse novo modelo, acompanharemos as aprovações (source, target) que nãorepresentammaisumrelacionamentorecíproco,massimquesourceaprove targetcomo um excelente cientista de dados (Figura 21-5). Precisaremos considerarestaassimetria:

endorsements=[(0,1),(1,0),(0,2),(2,0),(1,2),(2,1),(1,3),(2,3),(3,4),(5,4),(5,6),(7,5),(6,8),(8,7),(8,9)]

foruserinusers:user["endorses"]=[]#adicionaumalistaparaacompanharaprovaçõesdadasuser["endorsed_by"]=[]#adicionaoutraparacompanharaprovaçõesrecebidas

forsource_id,target_idinendorsements:users[source_id]["endorses"].append(users[target_id])users[target_id]["endorsed_by"].append(users[source_id])

Figura21-5.AredeDataSciencesterdeaprovações

apósoquepodemosfacilmenteencontraroscientistasdedadosmaisaprovados(most_endorsed)evendertodaessainformaçãoparaosrecrutadores:

endorsements_by_id=[(user["id"],len(user["endorsed_by"]))foruserinusers]

Page 373: Data Science do zero: Primeiras regras com o Python

1.2.3.

4.

sorted(endorsements_by_id,key=lambda(user_id,num_endorsements):num_endorsements,reverse=True)

Entretanto,“númerodeaprovações”éumamétricafácildemanipular.Tudoquevocê precisa fazer é criar contas falsas e fazer com que elas o aprovem. Oucombinarcomseusamigosparaaprovaremunsaosoutros(oqueparecequeosusuários0,1e2fizeram).

Uma métrica melhor para levar em consideração é quem aprova você.Aprovações de pessoas com muitas aprovações deveriam de alguma formacontar mais do que aprovações de pessoas com poucas aprovações. Essa é aessência do algoritmo PageRank, usado pelo Google para ordenar web sitesbaseadosemquaisoutrossitesestãoconectadosaeles,quaisaesteseporaívai.

(Se isso mais ou menos o lembra da ideia por trás da centralidade de vetorpróprio,vocêestácerto.)

Umaversãosimplificadaseparececomisto:

Háumtotalde1.0(ou100%)dePageRanknarede.InicialmenteestePageRankéigualmentedistribuídopelosnós.A cada passo, uma grande fração do PageRank do nó é distribuídaigualmenteentreosseuslinksdesaída.A cada passo, o resto dePageRankdonó é distribuído igualmente entretodososnós.defpage_rank(users,damping=0.85,num_iters=100):

#inicialmentedistribuiPageRankigualmentenum_users=len(users)pr={user["id"]:1/num_usersforuserinusers}

#estaéapequenafraçãodePageRank#quecadanórecebeacadaiteraçãobase_pr=(1-damping)/num_users

for__inrange(num_iters):next_pr={user["id"]:base_prforuserinusers}foruserinusers:

#distribuiPageRankparalinksdesaídalinks_pr=pr[user["id"]]*dampingforendorseeinuser["endorses"]:next_pr[endorsee["id"]]+=links_pr/len(user["endorses"])

Page 374: Data Science do zero: Primeiras regras com o Python

pr=next_pr

returnpr

PageRank(Figura21-6)identificaousuário4(Thor)comoocientistadedadoscommaisclassificações.

Figura21-6.AredeDataSciencesterdimensionadaporPageRank

Mesmo que ele tenha menos aprovações (2) do que usuários 0, 1 e 2, asaprovaçõesdelecarregamclassificaçõesdesuasaprovações.Alémdisso,ambosapoiadoresapoiaramapenasele,oque significaqueelenãoprecisoudividir aclassificaçãocommaisninguém.

Page 375: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosHámuitasoutrasnoçõesdecentralidade(http://en.wikipedia.org/wiki/Centrality)alémdasqueusamos(masessassãoasmaispopulares).NetworkX(http://networkx.github.io/)éumabibliotecaPythonparaanálisederede.Elapossuifunçõesparacomputarcentralidadesevisualizargráficos.Gephi(http://gephi.github.io/)éumaferramentadevisualizaçãoamada/odiadaderedebaseadanaferramentadevisualizaçãoGUI.

Page 376: Data Science do zero: Primeiras regras com o Python

CAPÍTULO22

SistemasRecomendadores

Ohnatureza,natureza,porqueéstãodesonesta,paraenviareshomenscomfalsasrecomendaçõesparaomundo!

—HenryFielding

Outro problema de dados comum é produzir algum tipo de recomendação. ANetflix recomenda filmes que você poderia querer assistir. A Amazonrecomenda produtos que você poderia querer comprar. O Twitter recomendausuáriosparavocêseguir.Nestecapítulo,veremosváriasformasdeusardadosparafazerrecomendações.

Em particular, veremos o conjunto de dados de users_interests que usamosanteriormente:

users_interests=[["Hadoop","BigData","HBase","Java","Spark","Storm","Cassandra"],["NoSQL","MongoDB","Cassandra","HBase","Postgres"],["Python","scikit-learn","scipy","numpy","statsmodels","pandas"],["R","Python","statistics","regression","probability"],["machinelearning","regression","decisiontrees","libsvm"],["Python","R","Java","C++","Haskell","programminglanguages"],["statistics","probability","mathematics","theory"],["machinelearning","scikit-learn","Mahout","neuralnetworks"],["neuralnetworks","deeplearning","BigData","artificialintelligence"],["Hadoop","Java","MapReduce","BigData"],["statistics","R","statsmodels"],["C++","deeplearning","artificialintelligence","probability"],["pandas","R","Python"],["databases","HBase","Postgres","MySQL","MongoDB"],["libsvm","regression","supportvectormachines"]

]

Page 377: Data Science do zero: Primeiras regras com o Python

Epensaremossobreoproblemaderecomendarnovosinteressesparaumusuáriobaseadoemseusinteressesatuaisespecíficos.

Page 378: Data Science do zero: Primeiras regras com o Python

CuradoriaManualAntesda Internet, quandovocêprecisavade recomendaçõesde livros,você iaatéabibliotecaondeumbibliotecárioestavadisponívelparasugerir livrosquefossemrelevantesaseusinteressesousimilaresalivrosquevocêgostou.

DadoumnúmerolimitadodeusuárioseinteressesdaDataSciencester,seriafácilparavocêpassarumatarderecomendandointeressesparacadausuário.Masessemétodo não escala particularmente bem e é limitado por seu conhecimentopessoal e imaginação. (Não que eu esteja sugerindo que seu conhecimentopessoal e imaginação sejam limitados.) Então vamos pensar sobre o quepodemosfazercomdados.

Page 379: Data Science do zero: Primeiras regras com o Python

RecomendandoOQueéPopularUmaabordagemfácilésimplesmenterecomendaroqueépopular:

popular_interests=Counter(interestforuser_interestsinusers_interestsforinterestinuser_interests).most_common()

queseparececom:[('Python',4),('R',4),('Java',3),('regression',3),('statistics',3),('probability',3),#...]

Tendocomputadoisso,podemosapenassugeriraumusuárioosinteressesmaispopularespelosquaiseleaindanãoestáinteressado:

defmost_popular_new_interests(user_interests,max_results=5):suggestions=[(interest,frequency)

forinterest,frequencyinpopular_interestsifinterestnotinuser_interests]

returnsuggestions[:max_results]

Então,sevocêéousuário1,cominteresses:["NoSQL","MongoDB","Cassandra","HBase","Postgres"]

recomendaríamosavocê:most_popular_new_interests(users_interests[1],5)

#[('Python',4),('R',4),('Java',3),('regression',3),('statistics',3)]

Sevocê forousuário3,que jáestá interessadoemmuitasdessascoisas,vocêreceberia:

[('Java',3),('HBase',3),('BigData',3),('neuralnetworks',2),('Hadoop',2)]

Claro,“muitaspessoassãointeressadasemPythonentãovocêtambémdeveria

Page 380: Data Science do zero: Primeiras regras com o Python

ser”nãoéumafrasedevendamuitopersuasiva.Sealguéménovoemnossositeenãosabemosnadasobreele,issoébasicamenteomelhorquepodemosfazer.Veremoscomopodemosmelhorarbaseandoasrecomendaçõesdecadausuárioemseusinteresses.

Page 381: Data Science do zero: Primeiras regras com o Python

FiltragemColaborativaBaseadanoUsuárioUma forma de levar em consideração os interesses do usuário é procurarusuários que são similares a ele e então sugerir as coisas nas quais aquelesusuáriossãointeressados.

Para fazer isso,precisaremosdeumamaneirademedirquãosimilaresosdoisusuários são. Aqui usaremos uma métrica chamada similaridade do cosseno.Dadosdoisvetores,vew,édefinidacomo:

defcosine_similarity(v,w):returndot(v,w)/math.sqrt(dot(v,v)*dot(w,w))

Elamedeo“ângulo”entrevew.Sevewapontamparaamesmadireção,entãoonumeradoreodenominadorsãoiguaisesuasimilaridadedocossenoéiguala1.Sevewapontamparadireçõesopostas,suasimilaridadedocossenoéiguala–1.Esevé0semprequewnãoé(evice-versa)entãodot(v,w)é0esuasimilaridadedocossenoserá0.

Quando aplicamos isso a vetores de 0s e 1s, cada vetor v representando osinteressesdeumusuário.v[i]será1seousuárioéespecificadocomoi-ésimointeresse,e0senão.Damesmaforma,“usuáriossimilares”significará“usuárioscujosvetoresdeinteressesquaseapontamparaamesmadireção”.Usuárioscominteresses idênticos terãosimilaridade1.Usuárioscominteressesnãoidênticosterão similaridade 0. Senão, a similaridade ficará no meio, com númerospróximos a 1 indicando “muito similar” e números próximos de 0 indicando“nãomuitosimilar".

Umbomlugarparacomeçaréjuntarosinteressesconhecidose(implicitamente)atribuiríndicesaeles.Podemosfazerissousandoumconjuntodecompreensãopara encontrar os interesses únicos, colocá-los em uma lista e ordená-los. Oprimeirointeressenalistaresultanteseráointeresse0eporaívai:

unique_interests=sorted(list({interestforuser_interestsinusers_interestsforinterestinuser_interests}))

Issonosdáumalistaquecomeça:['BigData',

Page 382: Data Science do zero: Primeiras regras com o Python

'C++','Cassandra','HBase','Hadoop','Haskell',#...]

Em seguida, queremos produzir um vetor “interesse” de 0s e 1s para cadausuário.Nóssóprecisamositerarpelalistaunique_interests,substituindoum1seousuáriopossuicadainteresse,e0senão:

defmake_user_interest_vector(user_interests):"""dadaumalistadeinteresses,produzaumvetorcujoi-ésimoelementoé1seunique_interests[i]estánalista,0"""return[1ifinterestinuser_interestselse0forinterestinunique_interests]

após isso, podemos criar uma matriz de interesses de usuário simplesmentemapeando(map_ping).estafunçãocontraalistadelistasdeinteresses:

user_interest_matrix=map(make_user_interest_vector,users_interests)

Agorauser_interest_matrix[i][j]éiguala1seousuárioiespecificouointeressej,e0senão.

Por termos um pequeno conjunto de dados, não é um problema computar assimilaridadesemparesentretodososnossosusuários:

user_similarities=[[cosine_similarity(interest_vector_i,interest_vector_j)forinterest_vector_jinuser_interest_matrix]forinterest_vector_iinuser_interest_matrix]

edepois,user_similarities[i][j]nosdáasimilaridadeentreiej.

Por exemplo, user_similarities[i] é 0,57, porque aqueles dois usuários demonstraminteresseemcomumemHadoop,JavaeBigData.Poroutrolado,user_similarities[0][8]ésomente0,19,porqueosusuários0e8demonstramapenasuminteresseemcomum:BigData;

Especificamente, user_similarities[i] é um vetor de similaridades do usuário i paracadaoutrousuário.Podemosusarissoparaescreverumafunçãoqueencontraosusuários mais similares a um usuário específico. Nos certificaremos de nãoincluir o próprio usuário, nem qualquer outro usuário com similaridade 0. E

Page 383: Data Science do zero: Primeiras regras com o Python

ordenaremososresultadosdomaissimilarparaomenos:defmost_similar_users_to(user_id):

pairs=[(other_user_id,similarity)#encontraoutrosforother_user_id,similarityin#usuárioscomenumerate(user_similarities[user_id])#similaridadeifuser_id!=other_user_idandsimilarity>0]#nãozero

returnsorted(pairs,#ordenakey=lambda(_,similarity):similarity,#maissimilaresreverse=True)#primeiro

Porexemplo,sechamarmosmost_similar_user(0),conseguimos:[(9,0.5669467095138409),(1,0.3380617018914066),(8,0.1889822365046136),(13,0.1690308509457033),(5,0.1543033499620919)]

Como usamos isso para sugerir novos interesses para um usuário? Para cadainteresse, podemos apenas somar similaridades de usuário dos outros usuáriosinteressados:

defuser_based_suggestions(user_id,include_current_interests=False):#somaassimilaridadessuggestions=defaultdict(float)forother_user_id,similarityinmost_similar_users_to(user_id):

forinterestinusers_interests[other_user_id]:suggestions[interest]+=similarity

#converte-asemumalistaordenadasuggestions=sorted(suggestions.items(),

key=lambda(_,weight):weight,reverse=True)

#e(talvez)excluiinteressesjáexistentesifinclude_current_interests:

returnsuggestionselse:

return[(suggestion,weight)forsuggestion,weightinsuggestionsifsuggestionnotinusers_interests[user_id]]

Sechamarmosuser_based_suggestions(0),osprimeirosinteressessugeridossão:[('MapReduce',0.5669467095138409),('MongoDB',0.50709255283711),('Postgres',0.50709255283711),('NoSQL',0.3380617018914066),

Page 384: Data Science do zero: Primeiras regras com o Python

('neuralnetworks',0.1889822365046136),('deeplearning',0.1889822365046136),('artificialintelligence',0.1889822365046136),#...]

Essasparecemsersugestõesdecentesparaalguémqueestáinteressadoem“BigData” e assuntos relacionados a bancos de dados. (Os pesos não sãosignificativosintrinsecamente;apenasosusamosparaordenar.)

Essaabordagemnãofuncionabemquandoonúmerodeitensficamuitogrande.LembredamaldiçãodadimensionalidadedoCapítulo12—emespaçosdevetorde grandes dimensões a maioria dos vetores estão distantes (e logo, apontamparadireçõesdiferentes).Istoé,quandoháumgrandenúmerodeinteresses,os“usuáriosmais similares” aumusuário específicopodemnão ser similaresnofinal.

ImagineumsitecomoodaAmazon,emquecompramosmilharesdeitensnasúltimasduasdécadas.Vocêpoderia tentar identificarusuários similares amimbaseadoempadrõesdecompras,masébemprovávelquenãoexistaninguémnomundoquecompreroupashistóricascomoasminhas.Quemquerquesejameucomprador “mais similar”, provavelmente não é similar amim e sua comprasquasecertamentedariamrecomendaçõesidiotasparamim.

Page 385: Data Science do zero: Primeiras regras com o Python

FiltragemColaborativaBaseadaemItensUma abordagem alternativa é computar similaridades entres interessesdiretamente. Nós podemos gerar sugestões para cada usuário agregandointeressesquesãosimilaresaseusinteressesatuais.

Para começar, queremos transpor amatriz de interesse do nosso usuário paraquefileirascorrespondamainteressesecolunascorrespondamausuários:

interest_user_matrix=[[user_interest_vector[j]foruser_interest_vectorinuser_interest_matrix]forj,_inenumerate(unique_interests)]

Com o que isso se parece? A linha j de interest_user_matrix é coluna j deuser_interest_matrix.Istoé,possui1paracadausuáriocomaqueleinteressee0paracadausuáriosemaqueleinteresse.

Porexemplo,unique_interests[0]éBigDataeinterest_user_matrix[0]tambémé:[1,0,0,0,0,0,0,0,1,1,0,0,0,0,0]

porqueusuários0,8e9indicaraminteresseemBigBata.

Agorapodemosusar similaridadedocossenonovamente.Se,precisamente,osmesmosusuáriosestãointeressadosemdoistópicos,suassimilaridadesserão1.Se nenhum dos dois usuários estiver interessado em ambos tópicos, asimilaridadeserá0:

interest_similarities=[[cosine_similarity(user_vector_i,user_vector_j)foruser_vector_jininterest_user_matrix]foruser_vector_iininterest_user_matrix]

Por exemplo, podemos encontrar os interesses mais similares a Big Data(interesse0)usando:

defmost_similar_interests_to(interest_id):similarities=interest_similarities[interest_id]pairs=[(unique_interests[other_interest_id],similarity)

forother_interest_id,similarityinenumerate(similarities)ifinterest_id!=other_interest_idandsimilarity>0]

returnsorted(pairs,key=lambda(_,similarity):similarity,reverse=True)

Page 386: Data Science do zero: Primeiras regras com o Python

oquesugereosseguintesinteressessimilares:[('Hadoop',0.8164965809277261),('Java',0.6666666666666666),('MapReduce',0.5773502691896258),('Spark',0.5773502691896258),('Storm',0.5773502691896258),('Cassandra',0.4082482904638631),('artificialintelligence',0.4082482904638631),('deeplearning',0.4082482904638631),('neuralnetworks',0.4082482904638631),('HBase',0.3333333333333333)]

Agora nós podemos criar recomendações para um usuário somando assimilaridadesdeinteressesparecidoscomosdele:

defitem_based_suggestions(user_id,include_current_interests=False):#somainteressessimilaressuggestions=defaultdict(float)user_interest_vector=user_interest_matrix[user_id]forinterest_id,is_interestedinenumerate(user_interest_vector):

ifis_interested==1:similar_interests=most_similar_interests_to(interest_id)forinterest,similarityinsimilar_interests:suggestions[interest]+=similarity

#ordenaporpesosuggestions=sorted(suggestions.items(),

key=lambda(_,similarity):similarity,reverse=True)

ifinclude_current_interests:returnsuggestions

else:return[(suggestion,weight)

forsuggestion,weightinsuggestionsifsuggestionnotinusers_interests[user_id]]

Para o usuário 0, isso gera as seguintes (aparentemente razoáveis)recomendações:

[('MapReduce',1.861807319565799),('Postgres',1.3164965809277263),('MongoDB',1.3164965809277263),('NoSQL',1.2844570503761732),('programminglanguages',0.5773502691896258),('MySQL',0.5773502691896258),('Haskell',0.5773502691896258),

Page 387: Data Science do zero: Primeiras regras com o Python

('databases',0.5773502691896258),('neuralnetworks',0.4082482904638631),('deeplearning',0.4082482904638631),('C++',0.4082482904638631),('artificialintelligence',0.4082482904638631),('Python',0.2886751345948129),('R',0.2886751345948129)]

Page 388: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosCrab(http://muricoca.github.io/crab/)éumframeworkparaconstruçãodesistemasrecomendadoresemPython.Graphlabtambémpossuiferramentasrecomendadoras(http://bit.ly/1MF9Tsy).OPrêmioNetflix(http://www.netflixprize.com)foiumacompetiçãofamosaparaconstruirummelhorsistemapararecomendarfilmesparausuáriosNetflix.

Page 389: Data Science do zero: Primeiras regras com o Python

CAPÍTULO23

BasesdeDadoseSQL

Amemóriaéamelhoramigaeapiorinimigadohomem.—GilbertParker

Os dados dos quais você precisará geralmente estarão em bases de dados,usandosistemasfeitosparaarmazenareconsultardadoseficientemente.Amaiorpartedessessistemasdebancosdedadossãorelacionais,comoOracle,MySQLeSQLServer,quearmazenamdadosem tabelasesãotipicamenteconsultadosusando Structured Query Language (SQL), uma linguagem declarativa paramanipulardados.

SQL é uma parte essencial do kit de ferramentas do cientista de dados.Nestecapítulo, criaremos NotQuiteABase, uma implementação Python de algo quenão é exatamente um banco de dados. Também cobriremos o básico de SQLenquanto mostramos como ele trabalham em nosso não exatamente banco dedados,queaformamais“dozero”quepudepensarparaajudaraentenderoquetais funcionalidades básicas estão fazendo. Minha esperança é que resolverproblemasemNotQuiteABasetraráumaboanoçãodecomovocêpoderesolverosmesmosproblemasusandoSQL.

Page 390: Data Science do zero: Primeiras regras com o Python

CREATETABLEeINSERTUmabasededados relacionaléumacoleçãode tabelas (ede relacionamentosentre elas). Uma tabela é uma simples coleção de linhas, não diferente dasmatrizes com as quais trabalhamos. Entretanto, uma tabela também possuiassociaçãocomumesquemafixoconstituídopornomesetiposdecolunas.

Porexemplo,imagineumconjuntodedadosuserscontendoparacadausuárioseuuser_id,nameenum_friends:

users=[[0,"Hero",0],[1,"Dunn",2],[2,"Sue",3],[3,"Chi",3]]

EmSQL,poderíamoscriaressatabelacom:CREATETABLEusers(

user_idINTNOTNULL,nameVARCHAR(200),num_friendsINT);

Notequeespecificamosqueuser_idenum_friends devemser inteiros (eque user_idnãopodeserNULL,oqueindicaumvalorfaltandoeécomonossoNone)equenome deveria ser uma cadeia de caracteres de tamanho 200 ou menor.NotQuiteABase não leva tipos em consideração mas se comporta como selevasse.

SQL é quase completamente insensível a endentação e capitalização. O estilo deendentaçãoecapitalizaçãoaquiéomeufavorito.SevocêcomeçaaaprenderSQL,vocêcertamenteencontraráoutrosexemplosestilizadosdiferentemente.

VocêpodeinseriraslinhascomdeclaraçõesINSERT:INSERTINTOusers(user_id,name,num_friends)VALUES(0,'Hero',0);

Note tambémquedeclaraçõesSQLprecisam terminar componto de vírgula equeSQLrequeraspaúnicaparasuasstrings.

EmNotQuiteABasevocêcriaráumaTablesimplesmenteespecificandoosnomes

Page 391: Data Science do zero: Primeiras regras com o Python

desuascolunas.Eparainserirumalinha,vocêusaráométodo insert()databela,quepegauma list devaloresde linhaqueprecisamestarnamesmaordemdosnomesdacolunadatabela.

Nosbastidores,armazenaremoscada linhacomoumdict (dicionário)denomesdecolunasparavalores.Umbancodedadosrealjamaisusariatalrepresentaçãomasfazê-latornaráNotQuiteABasemaisfácildetrabalhar:

classTable:def__init__(self,columns):

self.columns=columnsself.rows=[]

def__repr__(self):"""belarepresentaçãodatabela:colunaseentãolinhas"""returnstr(self.columns)+"\n"+"\n".join(map(str,self.rows))

definsert(self,row_values):iflen(row_values)!=len(self.columns):

raiseTypeError("wrongnumberofelements")row_dict=dict(zip(self.columns,row_values))self.rows.append(row_dict)

Porexemplo,poderíamosconfigurar:users=Table(["user_id","name","num_friends"])users.insert([0,"Hero",0])users.insert([1,"Dunn",2])users.insert([2,"Sue",3])users.insert([3,"Chi",3])users.insert([4,"Thor",3])users.insert([5,"Clive",2])users.insert([6,"Hicks",3])users.insert([7,"Devin",2])users.insert([8,"Kate",2])users.insert([9,"Klein",3])users.insert([10,"Jen",1])

Seagoravocêimprimirusers,verá:['user_id','name','num_friends']{'user_id':0,'name':'Hero','num_friends':0}{'user_id':1,'name':'Dunn','num_friends':2}{'user_id':2,'name':'Sue','num_friends':3}...

Page 392: Data Science do zero: Primeiras regras com o Python

••••

UPDATEÀsvezes,vocêprecisaatualizarosdadosque jáestãonobancodedados.Porexemplo,seDunnpossuiroutroamigo,vocêpodeprecisarfazerisso:

UPDATEusersSETnum_friends=3WHEREuser_id=1;

Osatributos-chavesão:QualtabelaatualizarQuaislinhasatualizarQuaiscamposatualizarQuaisdeveriamserosnovosvalores

Adicionaremos um método update similar em NotQuiteABase. Seu primeiroargumento seráum dict cujas chaves são colunaspara atualizar e cujosvaloressão os novos valores para aqueles campos. E seu segundo argumento é umpredicadoqueretornaTrueparalinhasquedeveriamseratualizadas,Falsesenão:

defupdate(self,updates,predicate):forrowinself.rows:

ifpredicate(row):forcolumn,new_valueinupdates.iteritems():

row[column]=new_value

eapósissopodemossimplesmentefazer:users.update({'num_friends':3},#estabelecenum_friends=3

lambdarow:row['user_id']==1)#emlinhasondeuser_id==1

Page 393: Data Science do zero: Primeiras regras com o Python

DELETEHá duas formas de excluir linhas de uma tabela em SQL. A forma perigosaapagatodasaslinhasdatabela:

DELETEFROMusers;

AformamenosperigosaadicionaumacláusulaWHEREeapenasapagaascolunasqueatendemaumacertacondição:

DELETEFROMusersWHEREuser_id=1;

ÉfácilacrescentaressafuncionalidadeanossaTable:defdelete(self,predicate=lambdarow:True):

"""apagatodasaslinhasquecombinamcomopredicadooutodasaslinhassenãohápredicado"""self.rows=[rowforrowinself.rowsifnot(predicate(row))]

Sevocêforneceuumafunçãopredicate(porexemplo,cláusulaWHERE),issoapagaapenas as linhas que a satisfazem. Se não forneceu uma, o predicado padrãosempreretornaTrueevocêapagarátodasaslinhas.

Porexemplo:users.delete(lambdarow:row["user_id"]==1)#apagalinhascomuser_id==1users.delete()#apagatodasaslinhas

Page 394: Data Science do zero: Primeiras regras com o Python

SELECTTipicamente você não inspeciona uma tabela SQL diretamente. Emvez disso,vocêaconsultacomumadeclaraçãoSELECT:

SELECT*FROMusers;--buscatodooconteúdoSELECT*FROMusersLIMIT2;--buscaasduasprimeiraslinhasSELECTuser_idFROMusers;--buscasomentecolunasespecíficasSELECTuser_idFROMusersWHEREname='Dunn';--buscasomentelinhasespecíficas

VocêtambémpodeusardeclaraçõesSELECTparacalcularcampos:SELECTLENGTH(name)ASname_lengthFROMusers;

NósdaremosanossaclasseTableummétodoselect()queretornaumanovaTable.Ométodoaceitadoisargumentosopcionais:

keep_columnsespecificaonomedascolunasquevocêquermanternoresultado.Sevocênãofornecê-la,oresultadocontémtodasascolunas.additional_columnséumdicionáriocujaschavessãonovosnomesdecolunasecujosvaloressãofunçõesespecificandocomocomputarosvaloresdenovascolunas.

Sevocênãofornecernenhumdosdois,vocêsimplesmentereceberiaumacópiadatabela:

defselect(self,keep_columns=None,additional_columns=None):

ifkeep_columnsisNone:#senenhumacolunaespecificada,keep_columns=self.columns#retornatodasascolunas

ifadditional_columnsisNone:additional_columns={}

#novatabelapararesultadosresult_table=Table(keep_columns+additional_columns.keys())

forrowinself.rows:new_row=[row[column]forcolumninkeep_columns]forcolumn_name,calculationinadditional_columns.iteritems():

new_row.append(calculation(row))result_table.insert(new_row)

returnresult_table

Nosso select() retorna uma nova tabela, enquanto o típico SQL SELECT() apenas

Page 395: Data Science do zero: Primeiras regras com o Python

produz algum tipo de resultado transitório (a menos que você explicitamenteinsiraosresultadosnatabela).

Nóstambémprecisaremosdosmétodoswhere()elimit().Ambossãobemsimples:defwhere(self,predicate=lambdarow:True):

"""retornaapenasaslinhasquesatisfazemopredicadofornecido"""where_table=Table(self.columns)where_table.rows=filter(predicate,self.rows)returnwhere_table

deflimit(self,num_rows):"""retornaapenasasprimeiraslinhasnum_rows"""limit_table=Table(self.columns)limit_table.rows=self.rows[:num_rows]returnlimit_table

e depois podemos facilmente construir equivalentes NotQuiteABase àsdeclaraçõesSQLprecedentes:

#SELECT*FROMusers;users.select()

#SELECT*FROMusersLIMIT2;users.limit(2)

#SELECTuser_idFROMusers;users.select(keep_columns=["user_id"])

#SELECTuser_idFROMusersWHEREname='Dunn';users.where(lambdarow:row["name"]=="Dunn")\

.select(keep_columns=["user_id"])

#SELECTLENGTH(name)ASname_lengthFROMusers;defname_length(row):returnlen(row["name"])

users.select(keep_columns=[],additional_columns={"name_length":name_length})

Observeque—diferentedorestodolivro—aquieuusobarrainvertida\paracontinuardeclaraçõesemmúltiplaslinhas.AcreditoqueissotornaapesquisadeNotQuiteABasemaisfácildelerdoquequalqueroutraforma.

Page 396: Data Science do zero: Primeiras regras com o Python

GROUPBYOutra operação SQL comum é GROUP BY, que agrupa linhas com valoresidênticosemcolunasespecificadaseproduzvaloresagregadoscomoMINeMAX

eCOUNTeSUM.Issodeveriarelembrarvocêdafunçãogroup_byde“ManipulandoDados”napágina129.

Porexemplo,vocêpodequererencontraronúmerodeusuárioseomenoruser_idparacadapossíveltamanhodenome:

SELECTLENGTH(name)asname_length,MIN(user_id)ASmin_user_id,COUNT(*)ASnum_usersFROMusersGROUPBYLENGTH(name);

CadacampoqueselecionamoscomSELECTprecisaestarnacláusulaGROUPBY(oquename_lengthé)ouserumacomputaçãoagregada(oquemin_user_idenum_userssão).

SQLtambémsuportaumacláusulaHAVINGquesecomportadeformasimilaracláusula WHERE com exceção de seu filtro, que é aplicado aos agregados(enquantoqueumWHEREfiltraria todasas linhasantesmesmodaagregaçãocomeçar).

Você pode querer saber o númeromédio de amigos de usuários cujos nomescomeçamcom letras específicasmas apenas ver os resultados para letras commédia correspondente maior que 1. (Sim, alguns desses exemplos sãomaquinados.)

SELECTSUBSTR(name,1,1)ASfirst_letter,AVG(num_friends)ASavg_num_friendsFROMusersGROUPBYSUBSTR(name,1,1)HAVINGAVG(num_friends)>1;

(FunçõesparatrabalharcomstringsvariampelasimplementaçõesSQL;algunsbancosdedadosusamSUBSTRINGououtracoisa.)

Vocêtambémpodecomputarototaldeagregados.Nestecaso,vocêabandonao

Page 397: Data Science do zero: Primeiras regras com o Python

1.

2.3.4.

GROUPBY:

SELECTSUM(user_id)asuser_id_sumFROMusersWHEREuser_id>1;

ParaacrescentaressafuncionalidadeàsTablesNotQuiteABase,adicionaremosummétodo group_by(). Ele usa os nomes das colunas que queremos agrupar, umdicionáriodefunçõesdeagregaçãoquevocêquerexecutaremcadagrupoeumpredicadohavingqueoperaemmúltiplaslinhas.

Osseguintespassossãorealizados:

Cria defaultdict para mapear tuples (de agrupação por valores) para linhas(contendoagrupaçãoporvalores).Lembre-sedequenãopodeusar listascomochavesdict;vocêtemqueusartuplas.Iterapelaslinhasdatabela,populandoodefaultdict.Criaumanovatabelacomascolunasdesaídacorretas.Itera por defaultdict e popula a tabela de saída, aplicando o filtro having, sehouver.

(Um banco de dados real quase certamente faria isso de uma forma maiseficiente.)

defgroup_by(self,group_by_columns,aggregates,having=None):

grouped_rows=defaultdict(list)

#populagruposforrowinself.rows:

key=tuple(row[column]forcolumningroup_by_columns)grouped_rows[key].append(row)

#tabelaresultantecomcolunasgroup_byeagregadosresult_table=Table(group_by_columns+aggregates.keys())

forkey,rowsingrouped_rows.iteritems():ifhavingisNoneorhaving(rows):

new_row=list(key)foraggregate_name,aggregate_fninaggregates.iteritems():new_row.append(aggregate_fn(rows))result_table.insert(new_row)

returnresult_table

Novamente, deixe-me ver como faríamos o equivalente às declarações SQL

Page 398: Data Science do zero: Primeiras regras com o Python

precedentes.Asmétricasname_lengthsão:defmin_user_id(rows):returnmin(row["user_id"]forrowinrows)

stats_by_length=users\.select(additional_columns={"name_length":name_length})\.group_by(group_by_columns=["name_length"],

aggregates={"min_user_id":min_user_id,"num_users":len})

Asmétricasfirst_letter:deffirst_letter_of_name(row):

returnrow["name"][0]ifrow["name"]else""

defaverage_num_friends(rows):returnsum(row["num_friends"]forrowinrows)/len(rows)

defenough_friends(rows):returnaverage_num_friends(rows)>1

avg_friends_by_letter=users\.select(additional_columns={'first_letter':first_letter_of_name})\.group_by(group_by_columns=['first_letter'],

aggregates={"avg_num_friends":average_num_friends},having=enough_friends)

euser_id_sumé:defsum_user_ids(rows):returnsum(row["user_id"]forrowinrows)

user_id_sum=users\.where(lambdarow:row["user_id"]>1)\.group_by(group_by_columns=[],

aggregates={"user_id_sum":sum_user_ids})

Page 399: Data Science do zero: Primeiras regras com o Python

ORDERBYFrequentemente,vocêdesejaráordenarseusresultados.Porexemplo,vocêpodequerersaber(alfabeticamente)osdoisprimeirosnomesdeseususuários:

SELECT*FROMusersORDERBYnameLIMIT2;

Issoéfácildeimplementarusandonossométodoorder_by()quepegaumafunçãoorder:

deforder_by(self,order):new_table=self.select()#criaumacópianew_table.rows.sort(key=order)returnnew_table

quepodemosusarcomosegue:friendliest_letters=avg_friends_by_letter\

.order_by(lambdarow:-row["avg_num_friends"])\

.limit(4)

O ORDER BY do SQL permite que você especifique ASC (ascendente) e DESC(descendentes)paracadacampo;aquiteríamosquecolocarissoemnossafunçãoorder.

Page 400: Data Science do zero: Primeiras regras com o Python

JOINOs bancos de dados relacionais são frequentemente normalizados, o quesignifica que ele são organizados para minimizar redundâncias. Por exemplo,quando trabalhamos com interesses de nossos usuários em Python podemosapenasdaracadausuárioumalistacontendoseusinteresses.

TabelasSQLtipicamentenãopodemconterlistas,entãoasoluçãotípicaécriaruma segunda tabela user_interests usando o relacionamento um-para-muitos entreuser_idseinteresses.EmSQLvocêpoderiafazer:

CREATETABLEuser_interests(user_idINTNOTNULL,interestVARCHAR(100)NOTNULL

);

masemNotQuiteABasevocêcriariaatabela:user_interests=Table(["user_id","interest"])user_interests.insert([0,"SQL"])user_interests.insert([0,"NoSQL"])user_interests.insert([2,"SQL"])user_interests.insert([2,"MySQL"])

Ainda há muita redundância — o “SQL” interessado está armazenado em doislugares diferentes. Emumbanco de dados real você talvez armazenasse user_id einterest_id na tabela user_interests, e então criaria uma terceira tabela interestsmapeandointerest_idparainterestafimdearmazenarcadanomedeinteresseapenasumavez.Aqui,elesapenastornariamnossosexemplosmaiscomplicadosainda.

Quandonossosdadosestãoemdiferentestabelas,comoosanalisamos?Fazendoajunçãodasduastabelas.JOINcombinalinhasnatabelaàesquerdacomlinhascorrespondentesna tabeladadireita,ondeo significadode“correspondente”ébaseadoemcomoespecificamosajunção.

Porexemplo,paraencontrarusuáriosinteressadosemSQLvocêconsultaria:SELECTusers.nameFROMusersJOINuser_interestsONusers.user_id=user_interests.user_idWHEREuser_interests.interest='SQL'

Page 401: Data Science do zero: Primeiras regras com o Python

JOINdizque,paracadalinhaemuser,deveríamosverouser_ideassociaralinhacomcadalinhaemuser_interestscontendoomesmouser_id.

NotequetivemosqueespecificarquaistabelasusarJOINequaiscolunasjuntar.Este é um INNER JOIN, que retorna as combinações de linhas (e apenas ascombinaçõesde linhas) que correspondemde acordo como critériode junçãoespecificado.

TambémháoLEFTJOIN,que—alémdascombinaçõesdelinhascorrespondentes— retorna uma linha para cada linha da tabela à esquerda sem linhascorrespondentes (nesse caso, os campos que deveriam vir da tabela a direitaseriamtodosNULL).

UsandoumLEFTJOIN,éfácilcontaronúmerodeinteressesdecadausuário:SELECTusers.id,COUNT(user_interests.interest)ASnum_interestsFROMusersLEFTJOINuser_interestsONusers.user_id=user_interests.user_id

LEFTJOINcertificaqueusuáriosseminteressesaindaterãolinhasnoconjuntodedados unidos (com valores NULL para campos provenientes de user_interests), eCOUNTapenascontavaloresquenãosãoNULL.

A implementação join() de NotQuiteABase será mais restritiva — elasimplesmentefazajunçãodeduastabelasemquaisquercolunasqueelastenhamemcomum.Mesmoassim,nãoétrivialescrever:

defjoin(self,other_table,left_join=False):

join_on_columns=[cforcinself.columns#colunasemifcinother_table.columns]#ambasastabelas

additional_columns=[cforcinother_table.columns#colunasapenasifcnotinjoin_on_columns]#natabelaàdireita

#todasascolunasdatabelaàesquerda+additional_columnsdatabelaàdireitajoin_table=Table(self.columns+additional_columns)

forrowinself.rows:defis_join(other_row):

returnall(other_row[c]==row[c]forcinjoin_on_columns)

other_rows=other_table.where(is_join).rows

#cadalinhaquecorrespondaaestaproduzumalinharesultadoforother_rowinother_rows:

Page 402: Data Science do zero: Primeiras regras com o Python

join_table.insert([row[c]forcinself.columns]+[other_row[c]forcinadditional_columns])

#senenhumalinhacorrespondeeháumleftjoin,saídacomNONEifleft_joinandnotother_rows:

join_table.insert([row[c]forcinself.columns]+[Noneforcinadditional_columns])

returnjoin_table

Então,podemosencontrarusuáriosinteressadosemSQLcom:sql_users=users\

.join(user_interests)\

.where(lambdarow:row["interest"]=="SQL")\

.select(keep_columns=["name"])

Econseguimospegaracontagemdeinteressescom:defcount_interests(rows):

"""contaquantaslinhasnãotêmdiferentedeinteresseNone"""returnlen([rowforrowinrowsifrow["interest"]isnotNone])

user_interest_counts=users\.join(user_interests,left_join=True)\.group_by(group_by_columns=["user_id"],

aggregates={"num_interests":count_interests})

EmSQLtambémháumRIGHTJOIN,quemantêmlinhasda tabelaadireitasemcorrespondenteseumFULLORDERJOIN,quemantêmlinhasdeambastabelasquenãopossuemcorrespondentes.Nãoimplementaremosnenhumadessas.

Page 403: Data Science do zero: Primeiras regras com o Python

SubconsultasEmSQL,vocêpodeselecionar(SELECT)de(ejuntar(JOIN))apartirderesultadosde consultas como se elas fossem tabelas. Então, se você quiser encontrar omenor user_id de alguém interessado em SQL, você poderia usar umasubconsulta. (Claro,vocêpoderiafazeromesmocálculousando JOIN,masnãoilustrariasubconsultas.)

SELECTMIN(user_id)ASmin_user_idFROM(SELECTuser_idFROMuser_interestsWHEREinterest='SQL')sql_interests;

Da forma como criamos NotQuiteABase, conseguimos isso de graça. Nossosresultadosdeconsultasãotabelas.

likes_sql_user_ids=user_interests\.where(lambdarow:row["interest"]=="SQL")\.select(keep_columns=['user_id'])

likes_sql_user_ids.group_by(group_by_columns=[],aggregates={"min_user_id":min_user_id})

Page 404: Data Science do zero: Primeiras regras com o Python

ÍndicesPara encontrar linhas contendo um valor específico (digamos, em que name é“Hero”),NotQuiteABase precisa inspecionar cada linha na tabela. Se a tabelapossuimuitaslinhas,issopodedemorarmuito.

Demaneiraparecida,nossoalgoritmojoinéextremamenteineficiente.Paracadalinhanatabelaaesquerda,eleinspecionacadalinhanatabelaadireitaparaverseéumacombinação.Comduasgrandestabelasissopodedemorareternamente.

Também, você geralmente gostaria de aplicar restrições a algumas de suascolunas.Porexemplo,natabelausersvocêprovavelmentenãoquerpermitirquedoisusuáriosdiferentestenhamomesmouser_id.

Índicesresolvemtodosessesproblemas.Seatabelauser_intereststivesseumíndiceemuser_id,umalgoritmo joinpoderiaencontrarcombinaçõesdiretamenteemvezdepesquisaratabelatoda.Seatabelauserstivesseum“único”índiceemuser_id,vocêreceberiaumerrosetentasseinserirumacópia.

Cadatabelaemumbancodedadospodeterumoumaisíndices,oquepermiteque você rapidamente encontre linhas por colunas-chave, eficientemente juntetabelaseexecuterestriçõesúnicasemcolunasoucombinaçõesdecolunas.

Planejareusaríndicesbeméumtipodeartenegra(oquevariadependendodobancodedadosespecífico),massevocêfazmuitotrabalhodebancodedados,valeapenaaprender.

Page 405: Data Science do zero: Primeiras regras com o Python

OtimizaçãodeConsultaLembre-sedaconsulta (consulta)paraencontrar todososusuários interessadosemSQL:

SELECTusers.nameFROMusersJOINuser_interestsONusers.user_id=user_interests.user_idWHEREuser_interests.interest='SQL'

EmNotQuiteABaseexiste(pelomenos)duasmaneirasdiferentesparaescreverestaconsul-ta.Vocêpoderiafiltraratabelauser_interestsfazendoajunção:

user_interests\.where(lambdarow:row["interest"]=="SQL")\.join(users)\.select(["name"])

Oupoderiafiltrarosresultadosdajunção:user_interests\

.join(users)\

.where(lambdarow:row["interest"]=="SQL")\

.select(["name"])

Vocêacabariacomosmesmosresultadosdequalquerforma,masfiltrarantesdejuntaréquasecertamentemaiseficiente,porquenestecasojointemmenoslinhasparaoperar.

Em SQL, você não se preocuparia com isso no geral. Você “declara” osresultadosquequeredeixaparaomotordeconsultaexecutá-los(eusaríndiceseficientemente).

Page 406: Data Science do zero: Primeiras regras com o Python

NoSQLUmatendênciarecenteembancosdedadosestádirecionadaabancosdedadosnãorelacionais“NoSQL”,quenãorepresentamdadosemtabelas.Porexemplo,MongoDB é um famoso banco de dados sem diagrama cujos elementos sãoarbitrariamentedocumentosJSONcomplexosemvezdelinhas.

Existembancosdedadosdecolunasquearmazenamdadosemcolunasemvezdelinhas(bonsquandoosdadostêmmuitascolunasmasasconsultasprecisamde poucas delas), armazenados valores-chave que são otimizados pararecuperaremvaloresúnicos(complexos)porsuaschaves,bancosdedadosparaarmazenarecriargráficostransversais,bancosdedadosquesãootimizadospararodar emmúltiplos datacenters, bancos de dados que são feitos para rodar namemória, bancos de dados para armazenar dados série temporal e centenas deoutros.

Anovidadede amanhãpode aindanemexistir, então eunãoposso fazer nadaalémdeinformarqueNoSQLexiste.Entãoagoravocêsabe.Eleexiste.

Page 407: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosSevocêgostariadefazerdownloaddeumbancodedadosrelacionalparabrincar,SQLite(http://www.sqlite.org)érápidoepequeno,enquantoMySQL(http://www.mysql.com)ePostgreSQL(http://www.postgresql.org)sãomaioresemaiscomplicados.Todossãogratuitosepossuemumagrandedocumentação.SevocêquiserexplorarNoSQL,MongoDB(http://www.mongodb.org)ébemsimplesparacomeçar,oquepodeserumabençãoouumamaldição.Eletambémtemumaboadocumentação.OartigosobreNoSQLnoWikipédia(http://en.wikipedia.org/wiki/NoSQL)quasecertamentepossuilinksparabancosdedadosquenemexistiamquandoestelivrofoiescrito.

Page 408: Data Science do zero: Primeiras regras com o Python

1.

2.3.

CAPÍTULO24

MapReduce

Ofuturojáchegou.Sónãofoidistribuídoigualmenteainda.—WilliamGibson

MapReduceéummodelodeprogramaçãopararealizarprocessamentoparaleloemgrandesconjuntosdedados.Emborasejaumatécnicapoderosa,suabaseérelativamentesimples.

Imagine que temos uma coleção de itens que gostaríamos de processar. Porexemplo,ositenspodemserlogsdewebsite,textosdelivrosvariados,arquivosde imagens ou qualquer outra coisa. Uma versão básica do algoritmoMapReduceconsistedosseguintespassos:

Useumafunçãomapperparatransformarcadaitememzerooumaispareschave-valor. (É chamado com frequência de funçãomap, porém já existeumafunçãoPythonchamadamapenãodevemosconfundirasduas.)Juntetodososparescomchavesidênticas.Useumafunçãoreduceremcadacoleçãodevaloresagrupadosparaproduzirvaloresdesaídaparaachavecorrespondente.

Isso tudo é um pouco abstrato, então vamos ver um exemplo específico. Hápoucas regras absolutas de data science mas uma delas é que seu primeiroexemploMapReducedeveenvolvercontagemdepalavras.

Page 409: Data Science do zero: Primeiras regras com o Python

Exemplo:ContagemdePalavrasDataSciencestercresceuparamilhõesdeusuários!Issoéótimoparaasegurançadoseuempregomasdificultaumpoucoaanálisederotina.

Porexemplo,avice-presidentedeConteúdoquerquevocêsaibasobreoqueaspessoasestão falandoemsuasatualizaçõesdestatus.Comoprimeira tentativa,vocêdecidecontaraspalavrasqueaparecem,paraquevocêpossaprepararumrelatosobreosmaisfrequentes.

Quandovocêtinhapoucascentenasdeusuáriosissoerasimplesdefazer:defword_count_old(documents):

"""contagemdepalavrassemusarMapReduce"""returnCounter(word

fordocumentindocumentsforwordintokenize(document))

Commilhõesdeusuários,oconjuntodedocuments(atualizaçõesdestatus)émuitograndepara caberno seu computador.Sevocêpuder encaixar issonomodeloMapReduce, pode usar alguma infraestrutura “big data” que seus engenheirosimplementaram.

Primeiro, queremos uma função que transforme um documento em umasequênciadepareschave-valor.Nósqueremosquenossasaídasejaagrupadaporpalavra, o que significa que as chaves deveriam ser palavras. E para cadapalavra, apenas emitiremos o valor 1 para indicar que este par corresponde aumaocorrênciadapalavra:

defwc_mapper(document):"""paracadapalavranodocumento,emite(word,1)"""forwordintokenize(document):

yield(word,1)

Pulandoopasso2porenquanto,imaginequeparaalgumapalavranóscoletamosuma lista de contas correspondentes às que emitimos. Então para produzir acontagemtotalparaaquelapalavra,apenasprecisamos:

defwc_reducer(word,counts):"""somaascontagensparaumapalavra"""yield(word,sum(counts))

Page 410: Data Science do zero: Primeiras regras com o Python

Retornando ao passo 2, agora precisamos coletar os resultados de wc_mapper efornecê-los a wc_reducer. Vamos pensar sobre o que faríamos em apenas umcomputador:

defword_count(documents):"""contaaspalavrasnosdocumentosdesaídausandoMapReduce"""

#lugarparaarmazenarvaloresagrupadoscollector=defaultdict(list)

fordocumentindocuments:forword,countinwc_mapper(document):

collector[word].append(count)

return[outputforword,countsincollector.iteritems()foroutputinwc_reducer(word,counts)]

Imaginequetemostrêsdocumentos[“datascience”,“bigdata”,“sciencefiction”].

Entãowc_mapper aplicado ao primeiro documento dá preferência aos dois pares(“data”,1)e(“science”,1).Depoisquenóspassamosportodosostrêsdocumentos,ocollectorcontém

{"data":[1,1],"science":[1,1],"big":[1],"fiction":[1]}

Entãowc_reducerproduzacontaparacadapalavra:[("data",2),("science",2),("big",1),("fiction",1)]

Page 411: Data Science do zero: Primeiras regras com o Python

PorqueMapReduce?Comomencionadoanteriormente,oprincipalbenefíciodeMapReduceéqueelepermite que distribuamos computações movendo o processamento aos dados.Imaginequequeremoscontarpalavrasembilhõesdedocumentos.

Nossa abordagem original (não MapReduce) requer que a máquina que estáfazendooprocessamentotenhaacessoatodososdocumentos.Issosignificaquetodos os documentos precisam viver na máquina ou ser transferidos para eladuranteoprocessamento.Maisimportante,significaqueamáquinapodeapenasprocessarumdocumentoporvez.

Possivelmente, ele processa até alguns por vez se tiver múltiplos núcleos e se ocódigo for reescrito para levar vantagem sobre eles. Mas mesmo assim, todos osdocumentosaindatêmquechegarnaquelamáquina.

Imagine agora que nossos bilhões de documentos estão espalhados por 100máquinas.Comainfraestruturacerta,podemosfazeroseguinte:

Façaamáquinarodaromapeador(mapper)emseusdocumentos,produzindomuitospares(chave-valor).Distribuaaquelespares(chave,valor)emumnúmerodemáquinasredutoras,certificandoqueosparescorrespondentesaqualquerchaveterminemnamesmamáquina.Façacadamáquinaredutoraagruparosparesporchaveeentãorodaroredutoremcadaconjuntodevalores.Retornecadapar(chave,saída).

O que é excelente nisso é que ele escala horizontalmente. Se dobrarmos onúmero de máquinas (ignorando certos custos fixos de rodar o sistemaMapReduce), então nossa computação deveria rodar aproximadamente duasvezesmaisrápido.Cadamáquinamapeadoraprecisaráfazerapenasmetadedotrabalho (assumindo que há chaves distintas o suficiente para distribuir aindamaisotrabalhodoredutor)eomesmoéverdadeparamáquinasredutoras.

Page 412: Data Science do zero: Primeiras regras com o Python

MapReduceMaisGeneralizadoSevocêpensarnissoporumminuto,todoocódigoespecíficoparacontagemdepalavranoexemploanteriorestácontidonasfunçõeswc_mapperewc_reducer. Issosignificaquecomalgumasmudançasnóstemosumframeworkmuitomaisgeral(queaindarodaemumaúnicamáquina):

defmap_reduce(inputs,mapper,reducer):"""rodaMapReducenasentradasusandomapperereducer"""collector=defaultdict(list)

forinputininputs:forkey,valueinmapper(input):

collector[key].append(value)

return[outputforkey,valuesincollector.iteritems()foroutputinreducer(key,values)]

Eentãopodemoscontarpalavrassimplesmenteusando:word_counts=map_reduce(documents,wc_mapper,wc_reducer)

Issonosdáaflexibilidadederesolverumagrandevariedadedeproblemas.

Antes de continuarmos, observe quewc_reducer está apenas somandoos valorescorrespondentesacadachave.Esse tipodeagregaçãoé tãocomumquevaleapenaabstrair:

defreduce_values_using(aggregation_fn,key,values):"""reduzumparchave-valoraplicandoaggregation_fnaosvalores"""yield(key,aggregation_fn(values))

defvalues_reducer(aggregation_fn):"""transformaumafunção(valores->saída)emumaredutoraquemapeia(chave,valor)->(chave,saída)"""returnpartial(reduce_values_using,aggregation_fn)

apósoquepodemosfacilmentecriar:sum_reducer=values_reducer(sum)max_reducer=values_reducer(max)min_reducer=values_reducer(min)count_distinct_reducer=values_reducer(lambdavalues:len(set(values)))

eassimpordiante.

Page 413: Data Science do zero: Primeiras regras com o Python

••

Exemplo:AnalisandoAtualizaçõesdeStatusAvice-presidentedeConteúdoficouimpressionadacomacontagemdepalavraseperguntaoquemaisvocêpodeaprenderdasatualizaçõesdestatusdaspessoas.Você consegue extrair um conjunto de dados de atualizações de status que separecemcom:

{"id":1,"username":"joelgrus","text":"Isanyoneinterestedinadatasciencebook?","created_at":datetime.datetime(2013,12,21,11,47,0),"liked_by":["data_guy","data_gal","mike"]}

Digamos que precisamos descobrir que dia da semana as pessoasmais falamsobredatascience.Paradescobririsso,apenascontaremosquantasatualizaçõesdedatascienceexistememcadadia.Issosignificaqueprecisaremosagruparpordiadasemana,entãoessaénossachave.Eseemitirmosumvalorde1paracadaatualizaçãoquecontém“datascience”,nóspodemossimplesmenteconseguirasomatotalusandosum:

defdata_science_day_mapper(status_update):"""produz(day_of_week,1)sestatus_updatecontém“datascience""""if"datascience"instatus_update["text"].lower():

day_of_week=status_update["created_at"].weekday()yield(day_of_week,1)

data_science_days=map_reduce(status_updates,data_science_day_mapper,sum_reducer)

Como um exemplo um pouco diferente, imagine que precisamos descobrir apalavramaiscomumquecadausuáriocolocaemsuasatualizaçõesdestatus.Hátrêsabordagenspossíveisquesurgemnamenteparamapper:

Coloqueonomedeusuárionachave;coloqueaspalavrasecontagensnosvalores.Coloqueapalavranachave;coloqueosnomesdeusuáriosnosvalores.Coloqueonomedeusuárioepalavranachave;coloqueascontagensnosvalores.

Sevocêpensarumpoucomaissobreisso,nósdefinitivamentequeremosagrupar

Page 414: Data Science do zero: Primeiras regras com o Python

por username, porque nós queremos considerar as palavras de cada pessoaseparadamente.Enósnãoqueremosagruparporpalavra, jáquenosso redutorprecisará ver todas as palavras para cada pessoa para descobrir qual é amaispopular.Issosignificaqueaprimeiraopçãoéaopçãocerta:

defwords_per_user_mapper(status_update):user=status_update["username"]forwordintokenize(status_update["text"]):

yield(user,(word,1))

defmost_popular_word_reducer(user,words_and_counts):"""dadaumasequênciadepares(palavra,contagem),retornaapalavracomamaiorcontagemtotal"""

word_counts=Counter()forword,countinwords_and_counts:

word_counts[word]+=count

word,count=word_counts.most_common(1)[0]

yield(user,(word,count))

user_words=map_reduce(status_updates,words_per_user_mapper,most_popular_word_reducer)

Oupoderíamosdescobrironúmerodediferentes curtiçõesde statuspara cadausuário:

defliker_mapper(status_update):user=status_update["username"]forlikerinstatus_update["liked_by"]:

yield(user,liker)

distinct_likers_per_user=map_reduce(status_updates,liker_mapper,count_distinct_reducer)

Page 415: Data Science do zero: Primeiras regras com o Python

Exemplo:MultiplicaçãodeMatrizLembre-sede“MultiplicaçãodeMatriz”napágina260quedadaumamatrizAm×neumamatrizBm×k,podemosmultiplicá-lasparaformarumamatrizCm×k,emqueoelementodeCnafileiraiecolunajédadopor:

Comovimos,umaforma“natural”derepresentarumamatrizm×nécomumalistadelistas,ondeoelementoAijéoelementoj-ésimodalistai-ésima.

Masgrandesmatrizes,àsvezes,sãoesparsas,oquesignificaqueamaioriadeseuselementos são iguais a zero.Paragrandesmatrizes esparsas,uma listadelistas pode ser uma representação inútil. Uma representaçãomais compacta éuma lista de tuples (name, i, j, value) onde name identifica amatriz e onde i, j, valueindicaalocalizaçãocomvalornãozero.

Porexemplo,umamatrizbilhãoxbilhãopossuiumquintilhãodeentradas,quenãoseriamfáceisdearmazenaremumcomputador.Masseháapenasalgumasentradasnãozeroemcadafileira,essarepresentaçãoalternativaémuitomenor.

Dado esse tipo de representação, podemos usar MapReduce para executar amultiplicaçãodematrizdeumamaneiradistribuída.

Paramotivarnossoalgoritmo,notequecadaelementoAijéusadoapenasparacomputarelementosdeCna fileira i,ecadaelementoBij éusadoapenasparacomputaroselementosdeCnacolunaj.Nossoobjetivoseráparacadasaídadonosso reducer serumaentradaúnicadeC,oquesignificaqueprecisaremosquenossomapper emitachaves identificandoumaúnicaentradadeC. Isso sugereoseguinte:

defmatrix_multiply_mapper(m,element):"""méadimensãocomum(colunasdeA,linhasdeB)elementoéumatupla(matrix_name,i,j,value)"""name,i,j,value=element

ifname=="A":#A_ijéaj-ésimaentradanasomadecadaC_ik,k=1..mforkinrange(m):

#agrupadacomoutrasentradasparaC_ik

Page 416: Data Science do zero: Primeiras regras com o Python

yield((i,k),(j,value))

else:#B_ijéai-ésimaentradanasomadecadaC_kjforkinrange(m):

#agrupadacomoutrasentradasparaC_kyield((k,j),(i,value))

defmatrix_multiply_reducer(m,key,indexed_values):results_by_index=defaultdict(list)forindex,valueinindexed_values:

results_by_index[index].append(value)

#somatodososprodutosdasposiçõescomdoisresultadossum_product=sum(results[0]*results[1]

forresultsinresults_by_index.values()iflen(results)==2)

ifsum_product!=0.0:yield(key,sum_product)

Porexemplo,sevocêtivesseasduasmatrizesA=[[3,2,0],

[0,0,0]]

B=[[4,-1,0],[10,0,0],[0,0,0]]

vocêpoderiareescrevê-lascomotuplas:entries=[("A",0,0,3),("A",0,1,2),

("B",0,0,4),("B",0,1,-1),("B",1,0,10)]mapper=partial(matrix_multiply_mapper,3)

reducer=partial(matrix_multiply_reducer,3)

map_reduce(entries,mapper,reducer)#[((0,1),-3),((0,0),32)]

Isso não é interessante emmatrizes pequenasmas se você tivessemilhões defileirasemilhõesdecolunas,issopoderiaajudarmuito.

Page 417: Data Science do zero: Primeiras regras com o Python

UmAdendo:CombinadoresUmacoisaquevocêprovavelmentenotouéquemuitosdosnossosmapeadoresparecemincluirummontedeinformaçãoextra.Porexemplo,aocontarpalavras,em vez de emitir (word, 1) e somar os valores, poderíamos emitir (word, None) eapenaspegarotamanho.

Uma razão pela qual não fizemos isso é que, na configuração distribuída, àsvezesqueremosusarcombinadoresparareduziraquantidadededadosquetêmque ser transferidos de máquina para máquina. Se uma de nossas máquinasmapeadorasvêmapalavra“data”500vezes,podemosdizerparaelacombinar500 instâncias de (“data”, 1) em uma única (“data”, 500) antes de entregar para amáquina redutora. Isso resulta emmuito menos dados sendo movidos, o quepodedeixarnossoalgoritmoaindamaisrápido.

Pela forma como escrevemos nosso redutor, ele lidaria com esses dadoscombinadoscorretamente.(Setivéssemosescritousandolen,nãofaria.)

Page 418: Data Science do zero: Primeiras regras com o Python

ParaMaisEsclarecimentosOsistemaMapReducemaisusadoéHadoop(http://hadoop.apache.org),quetemméritoemmuitoslivros.HáváriasdistribuiçõescomerciaisenãocomerciaiseumgrandeecossistemadeferramentasrelacionadasaHadoop.Parausá-lo,vocêdeveconfigurarseuprópriocluster(ouencontraralguémquedeixevocêusarodele),oquenãoénecessariamenteumatarefaparaosfracosdecoração.MapeadoreseredutoresHadoopsãocomumenteescritosemJava,emboraexistaumafacilidadeconhecidacomo“Hadoopstreaming”quelhepermiteescrevê-lasemoutraslinguagens(incluindoPython).AAmazonofereceumserviçoElasticMapReduce(http://aws.amazon.com/elasticmapreduce/)quepodecriaredestruirclusters,cobrandodevocêapenaspelotempoquevocêosutiliza.mrjobéumpacotePythonparainterfacecomHadoop(ouElasticMapReduce).TrabalhosHadoopsãotipicamentedealtalatência,oqueostornaumaescolharuimparaanálisesem“temporeal”.Háváriasferramentasde“temporeal”construídassobreHadoop,mastambémhámuitoframeworksalternativosqueestãoficandomaispopulares.DoisdosmaispopularessãoSpark(http://spark.apache.org/)eStorm(http://storm.incubator.apache.org/).Agoraébemprovávelqueanovidadedodiasejaalgumframeworkquenemexistiaquandoestelivrofoiescrito.Vocêteráquedescobrirsozinho.

Page 419: Data Science do zero: Primeiras regras com o Python

CAPÍTULO25

VáemFrenteePratiqueDataScience

Eagora,maisumavez,euordenoaminhahediondaprolequesigaemfrenteeprospere.—MaryShelley

Paraondevocêvaidaqui?Supondoqueeunãoassusteivocêcomdatascience,háumgrandenúmerodecoisasquevocêdeveriaaprenderemseguida.

Page 420: Data Science do zero: Primeiras regras com o Python

IPythonNós mencionamos IPython (http://ipython.org/) anteriormente no livro. EleforneceumshellcommuitomaisfuncionalidadesdoqueoshellpadrãoPythone adiciona “funções mágicas” que permitem que você (dentre outras coisas)copie e cole o código (que é normalmente complicado pela combinação deformataçãocomlinhasvaziaseespaçosembrancos)erodescriptsdedentrodoshell.

Tornar-se um especialista em IPython facilitarámais a sua vida. (AtémesmoaprenderumpouquinhodeIPythontornarásuavidamuitomaisfácil.)

Alémdomais,elepermitequevocêcrie“cadernos”combinandotexto,códigoPythonvivoevisualizaçõesquevocêpodecompartilharcomoutraspessoas,ouapenasmantercomoumdiáriodoquevocêfez(Figura25-1).

Figura25-1.UmcadernoIPython

Page 421: Data Science do zero: Primeiras regras com o Python

MatemáticaAo longo do livro, nós exploramos álgebra linear (Capítulo 4), estatística(Capítulo5),probabilidade(Capítulo6)easpectosvariadosdoaprendizadodemáquina.

Para ser um bom cientista de dados, você deve sabermuitomais sobre essestópicos e eu encorajo você a tentar estudar cada um deles, usando os livrosrecomendadosaofinaldoscapítulos,seuslivrospreferidos,cursosonlineouatémesmocursospresenciais.

Page 422: Data Science do zero: Primeiras regras com o Python

NãoDoZeroImplementarcoisas“dozero”éótimoparaentendercomoelasfuncionam.Masgeralmente não é ótimo em performance (a não ser que você os estejaimplementandoespecificamentecomperformanceemmente),facilidadedeuso,respostarápidaoutratamentodeerros.

Na prática, você vai querer usar bibliotecas bem projetadas que implementemsolidamenteosessenciais.(Minhapropostaoriginalparaestelivroenvolviaumasegunda metade “agora vamos aprender as bibliotecas” que a O'Reilly,felizmente,vetou.)

NumPyNumPy (http://www.numpy.org) (para “Numeric Python”) fornece facilidadespara fazer computação científica “real”. Ele contém arrays que desempenhammelhor do que nossos vetores list, matrizes que desempenhammelhor do quenossasmatrizeslist-of-listeváriasfunçõesnuméricasparatrabalharcomeles.

NumPyéumpilarparamuitasoutrasbibliotecas,oquetornaseuconhecimentoespecialmentevalioso.

pandaspandas (http://pandas.pydata.org) fornece estruturas de dados adicionais paratrabalhar com conjuntos de dados em Python. Sua abstração primária é oDataFrame, que é conceitualmente similar à classe NotQuiteABase Table queconstruímos no Capítulo 23, mas com muito mais funcionalidades e melhorperformance.

Se você usar Python para analisar, dividir, agrupar oumanipular conjuntos dedados,pandasumaferramentadevalorinestimável.

scikit-learnscikit-learn (http://scikit-learn.org) provavelmente é a biblioteca mais popularparafazeraprendizadodemáquinaemPython.Elacontémtodososmodelosque

Page 423: Data Science do zero: Primeiras regras com o Python

implementamos e muitos mais que não usamos. Em um problema real, vocêjamais construiria uma árvore de decisão do zero; você faria scikit-learn fazer otrabalhopesado.Emumproblemareal,vocêjamaisescreveriaumalgoritmodeotimizaçãoàmão;vocêcontariaquescikit-learnjáestivesseusandoummuitobom.

Sua documentação contém muitos exemplos (http://scikit-learn.org/stable/auto_examples/) do que pode fazer (e o que o aprendizado demáquinapodefazer).

VisualizaçãoOs gráficos matplotlib que criamos foram limpos e funcionais mas nãoparticularmenteestilosos (enada interativos).Sevocêquiser seaprofundaremvisualizaçãodedados,vocêpossuimuitasopções.

Aprimeiraéexplorarmaismatplotlib,cujascaracterísticasnósjáfalamos.Seuwebsite contém muitos exemplos (http://matplotlib.org/examples/) de suasfuncionalidades e uma galeria (http://matplotlib.org/gallery.html) de alguns deseus exemplosmais interessantes. Se você quiser criar visualizações estáticas,esteé,provavelmente,seupróximopasso.

Vocêtambémdeveriaverificarseaborn,queéumabibliotecaque(dentreoutrascoisas)tornamatplotlibmaisatraente.

Se você quiser criar visualizações interativas que você possa compartilhar naWeb,aopçãoóbviaéD3.js,umabibliotecaJavaScriptparacriar“DocumentosDirecionados por Dados” (estes são os três Ds). Mesmo que você não saibamuito JavaScript, é possível pegar exemplos da galeria D3 e ajustá-los paratrabalharem com seus dados. (Bons cientistas copiam da galeria D3. ÓtimoscientistasroubamdagaleriaD3.)

MesmoquevocênãotenhainteresseemD3,apenasdarumaolhadanagaleriaébemeducativoparavisualizaçãodedados.

BokehéumprojetoquetrazfuncionalidadedeestiloD3paraPython.

RMesmoquevocêpossasesairbemsemaprenderR,muitoscientistasdedadose

Page 424: Data Science do zero: Primeiras regras com o Python

projetosdedatascienceusamisso,entãovaleapenaaomenossefamiliarizar.

Emparte, issoéparaquepossaentenderpostagensdeblogsbaseadasemReexemplos e código; em parte, isso é para lhe ajudar a apreciar a(comparativamente)elegânciadePython;e,emparte,issoéparavocêsetornarumparticipantemelhorinformadonaeternaguerra“RversusPython”.

NomundonãofaltamtutoriasdeR,cursosdeRelivrosdeR.EuescutoboascoisassobreHands-onProgrammingwithRenãoapenasporquetambéméumlivroO'Reilly.(Ok,emgrandeparteporserumlivroO'Reilly.)

Page 425: Data Science do zero: Primeiras regras com o Python

EncontreDadosSe você está fazendo data science como parte do seu trabalho, vocêprovavelmente conseguirá dados como parte do seu trabalho (mas nãonecessariamente). E se você estiver fazendo data science por diversão?Dadosestãoemtodososlugares,masestessãoalgunspontosdepartida:

Data.govéoportaldedadosdogoverno.Sevocêquiserdadosdequalquercoisaarespeitodogoverno(oquepareceseraondadomomento)éumbomlugarparacomeçar.redditpossuialgunsfóruns,r/datasetser/data,quesãolugaresparadescobrireperguntarsobredados.AAmazonmantémumacoleçãodeconjuntodedadospúblicosqueelesgostariamquevocêanalisasseusandoseusprodutos(masquevocêpodeanalisarcomqualquerprodutoquequiser).RobbSeatonpossuiumalistadeconjuntosdedadosselecionadosemseublog.Kaggleéumsitequefazcompetiçõesdedatascience.Eununcaconseguientraremuma(eunãotenhoumespíritomuitocompetitivo)masvocêpodetentar.

Page 426: Data Science do zero: Primeiras regras com o Python

PratiqueDataScienceDar uma olhada em catálogos de dados é bom, mas os melhores projetos (eprodutos)sãoaquelequedãoumacertacoceira.Estesforamalgunsqueeufiz.

HackerNewsHackerNewséumsitedeagregaçãodenotíciasedediscussãosobrenotíciasrelacionadasatecnologia.Elecolecionamuitosartigos,muitosdosquaisnãosãointeressantesparamim.

Muitos anos atrás, eu construí um classificador de históriaHackerNews parapreverseeuestariainteressadoounãoemumahistória.IssonãofoimuitobomcomosusuáriosdeHackerNews,queficarammagoadoscomofatodealguémnãoestarinteressadoemtodasashistóriasnosite.

Issoenvolveurotularmuitashistórias(paraconseguirumpoucodetreinamento),escolheratributosdehistórias(porexemplo,palavrasemumtítulo,edomíniosde links) e treinarumclassificadorNaiveBayesnãomuitodiferentedonossofiltrodespam.

Porrazõesagoraperdidasnahistória,euoconstruíemRuby.Aprendacomosmeuserros.

CarrosdeBombeirosEumoroemumagranderuanocentrodeSeattle,entreumaestaçãodocorpodebombeiros e a maioria dos incêndios da cidade (ou é o que parece). Com opassar dos anos, eu desenvolvi um interesse recreacional pelo Corpo deBombeirosdeSeattle.

Felizmente(deumaperspectivadedados)elesmantêmumsiteemtemporeal911quelistacadaalarmedeincêndiocomoscarrosdebombeirosenvolvidos.

Eentão,parasatisfazermeuinteresse,eujunteimuitosanosdedadosdealarmese realizei uma análise de rede social dos carros de bombeiros. Entre outrascoisas, isso exigiu que eu inventasse uma noção de centralidade específica decarrodebombeiros,queeuchameideTruckRank.

Page 427: Data Science do zero: Primeiras regras com o Python

CamisetasEutenhoumafilhajovemeumafonteincessantedefrustraçãoparamimdurantesua infância foiqueamaioriadas“blusas femininas”sãosemgraça,enquanto“camisasmasculinas”sãomuitodivertidas.

Emparticular,estavaclaroparamimquehaviaumadiferençaentreascamisaspara bebês meninos e bebês meninas. Então, eu me perguntei se eu poderiatreinarummodeloparareconheceressasdiferenças.

Resultado:Eupude.

Issoenvolveufazerdownloaddeimagensdecentenasdecamisas,minimizando-asparaomesmotamanho,torná-lasemvetoresdecorespixeleusarregressãologísticaparaconstruirumclassificador.

Uma abordagem parecia simples onde as cores eram apresentadas em cadacamisa; uma segunda abordagem encontrou os 10 componentes principais dosvetoresda imagemdacamisaeclassificoucadacamisausandosuasprojeçõesemumespaçodimensional10abrangendoas“autocamisetas”(Figura25-2).

Figura25-2.Autocamisetascorrespondentesaoprimeirocomponenteprincipal

EVocê?Oqueinteressavocê?Quaisperguntastiramseusono?Procureporumconjuntodedadosefaçaumpoucodedatascience.

Page 428: Data Science do zero: Primeiras regras com o Python

SobreoAutorJoelGruséengenheirodesoftwarenoGoogle.Játrabalhoucomocientistadedadosemdiversasempresas.MoraemSeattle,onderegularmentecompareceaencontrosdeestudosemdatascience.Eleusaseublogcompoucafrequênciaemjoelgrus.comeusaoTwitterodiainteiroem@joelgrus.

Page 429: Data Science do zero: Primeiras regras com o Python

ColophonO animal na capa deData Science do Zero é um lagópode branco (Lagopusmuta). Esse pássaro de tamanho médio da família do galo-silvestre é apenaschamadode“lagópode”noReinoUnidoenoCanadáede“galodasneves”nosEstadosUnidos.O lagópodebranco é sedentário, e se reproduz pelo ártico daEurásia,naAméricadoNorteenaGroenlândia.Elepreferehabitatsdesertoseisolados como as montanhas da Escócia, os Pireneus, os Alpes, os Urais, aCordilheiraPamir,Bulgária,asmontanhasdeAltaianeosAlpesJaponeses.Elecome bétulas e botões de salgueiros, mas também se alimenta de sementes,flores,folhasefrutasvermelhas.Osjovenslagópodesbrancoscomeminsetos.

Os lagópodes brancos machos não possuem os ornamentos típicos do galo-silvestreexcetopelacrista,eéusadaparafazeracorteedesafiaroutrosmachos.Muitosestudosmostraramqueexisteumacorrelaçãoentreotamanhodacristaeos níveis de testosterona nos machos. Suas penas mudam do inverno para aprimavera e verão, trocando de branco para marrom, fornecendo um tipo decamuflagem sazonal. Os machos em reprodução possuem asas brancas e aspartes de cima cinzas exceto no inverno, no qual sua plumagem écompletamentebrancaexcetopeloseurabo.

Comseismesesde idade,o lagópode se torna sexualmentemaduro; é comumumataxadereproduçãodeseisgalosportemporadadereprodução,oqueajudaa proteger a população dos fatores externos como a caça. Também espantamuitospredadoresdevidoaoseuhabitatremotoeécaçadoprincipalmentepelaságuiasdouradas.

O lagópode branco é o principal alimento dos festivais de comida islandeses.Caçarolagópodebrancofoiproibidoem2003e2004devidoaodeclíniodesuapopulação. Em 2005, a caça foi liberada novamente com restrição em algunsdias.Todoocomérciodolagópodebrancoéilegal.

MuitosdosanimaisdascapasdaO’Reillysãoanimaisemextinção; todoselessãoimportantesparaomundo.Paraaprendermaissobrecomovocêpodeajudar,váemanimals.oreilly.com.

AimagemdacapaédaHistóriaNaturaldeCassell.AsfontesdacapasãoURW

Page 430: Data Science do zero: Primeiras regras com o Python

TypewritereGuardianSans.AfontedotextoéaAdobeMinionPro;afontedocabeçalhoéaAdobeMyriadCondensedeafontedoscódigoséaDaltonMaagdaUbuntuMono.

Page 431: Data Science do zero: Primeiras regras com o Python
Page 432: Data Science do zero: Primeiras regras com o Python
Page 433: Data Science do zero: Primeiras regras com o Python

HTML,XHTMLeCSSParaLeigosTittle,Ed9788550804200412páginas

Compreagoraeleia

Bemvindoàspossibilidadesdesenfreadas,malucasemaravilhosasdaWorldWideWeb,ou,mais,simplesmente,aweb.HTML,XHTMLeCSSParaLeigos,revelaosdetalhesdaslinguagensdemarcaçãoquesãoaveiadaweb–aHypertextMarkupLanguage(HTML)esuaprima,XHTM,juntocomalinguagemdaCascadingStyleSheet(CSS),usadaparafazercomqueoutrascoisaspareçamboas.HTMLeXHTML(usamos(X)HTMLnestelivroparafazerreferênciaàsduas)eCSSsãousadasparacriarpáginasweb.Aprenderausá-lasocolocanogrupodosautoresedesenvolvedoresdeconteúdoweb.Pensenesselivrocomoumguiaamigáveleacessívelparadominar(X)HTMLECSS!

Compreagoraeleia

Page 434: Data Science do zero: Primeiras regras com o Python
Page 435: Data Science do zero: Primeiras regras com o Python

12regrasparaavidaPeterson,JordanB.9788550804002448páginas

Compreagoraeleia

Aclamadopsicólogoclínico,JordanPetersonteminfluenciadoacompreensãomodernasobreapersonalidadee,agora,setransformouemumdospensadorespúblicosmaispopularesdomundo,comsuaspalestrassobretópicosquevariamdabíblia,àsrelaçõesamorosaseàmitologia,atraindodezenasdemilhõesdeespectadores.Emumaerademudançassemprecedentesepolarizaçãodapolítica,suamensagemfrancaerevigorantesobreovalordaresponsabilidadeindividualedasabedoriaancestraltemecoadoemtodososcantosdomundo.Bem-humorado,surpreendenteeinformativo,dr.Petersonnoscontaporquemeninosemeninasandandodeskatedevemserdeixadosempaz,queterríveldestinoaguardaaquelesquecriticamcommuitafacilidadeeporquevocêsempredeveacariciargatosaoencontrarumnarua.Oqueosistemanervosodashumildeslagostastemanosdizersobrearelaçãoentremanterascostaseretas(eosombrosparatrás)eosucessonavida?Porqueosantigosegípciosveneravamacapacidadedeatençãocomoseudeusmaissupremo?Queterríveiscaminhosaspessoaspercorremquandosetornamressentidas,arrogantesevingativas?Nestelivro,eleoferecedozeprincípiosprofundosepráticossobrecomoviverumavidacomsignificado.

Compreagoraeleia

Page 436: Data Science do zero: Primeiras regras com o Python
Page 437: Data Science do zero: Primeiras regras com o Python

DesignThinkingBrown,Tim9788550803869272páginas

Compreagoraeleia

EstelivrointroduzaideiadeDesignThinking,umprocessocolaborativoqueusaasensibilidadeeatécnicacriativaparasuprirasnecessidadesdaspessoasnãosócomoqueétecnicamentevisível,mascomumaestratégiadenegóciosviável.Emresumo,oDesignThinkingconvertenecessidadeemdemanda.Éumaabordagemcentradanoaspectohumanodestinadaaresolverproblemaseajudarpessoaseorganizaçõesaseremmaisinovadorasecriativas.Escritonumalinguagemleveeembasada,estenãoéumlivrodedesignersparadesigners,esimumaobraparalíderescriativosqueestãosempreembuscadealternativasviáveis,tantofuncionalquantofinanceiramente,paraosnegócioseparaasociedade.Nestelivro,TimBrown,CEOdacelebradaempresadeinovaçãoedesignIDEO,nosapresentaodesignthinking.Odesignnãoselimitaacriarobjetoselegantesouembelezaromundoanossoredor.Osmelhoresdesignerscompatibilizamaexigênciacomautilidade,asrestriçõescomapossibilidadeeanecessidadecomademanda.

Compreagoraeleia

Page 438: Data Science do zero: Primeiras regras com o Python
Page 439: Data Science do zero: Primeiras regras com o Python

PaiRico,PaiPobre-Ediçãode20anosatualizadaeampliadaT.Kiyosaki,Robert9788550801483336páginas

Compreagoraeleia

Aescolapreparaascriançasparaomundoreal?Essaéaprimeiraperguntacomaqualoleitorsedeparanestelivro.Orecadoéousadoedireto:boaformaçãoenotasaltasnãobastamparaassegurarosucessodealguém.Omundomudou;amaioriadosjovenstemcartãodecrédito,antesmesmodeconcluirosestudos,enuncateveaulasobredinheiro,investimentos,jurosetc.Ouseja,elesvãoparaaescola,mascontinuamfinanceiramenteimproficientes,despreparadosparaenfrentarummundoquevalorizamaisasdespesasdoqueapoupança.Paraoautor,oconselhomaisperigosoquesepodedaraumjovemnosdiasdehojeé:"Váparaaescola,tirenotasaltasedepoisprocureumtrabalhoseguro."Ofatoéqueagoraasregrassãooutras,enãoexistemaisempregogarantidoparaninguém.PaiRico,PaiPobredemonstraqueaquestãonãoéserempregadoouempregador,masterocontroledoprópriodestinooudelegá-loaalguém.ÉessaatesedeRobertKiyosakinestelivrosubstancialevisionário.Paraele,aformaçãoproporcionadapelosistemaeducacionalnãopreparaosjovensparaomundoqueencontrarãodepoisdeformados.Ecomoospaispodemensinaraosfilhosoqueaescolarelega?EssaéoutradasmuitasperguntasqueoleitorencontraemPaiRico,PaiPobre.Nessesentido,apropostadoautoréfacilitaratarefadospais.Quementendedecontabilidadedeveesquecerseusconhecimentosacadêmicos,poismuitasdasteoriasexpostasporRobertKiyosakicontrariamosprincípioscontábeiscomumenteaceitos,eapresentam

Page 440: Data Science do zero: Primeiras regras com o Python

umavaliosaemodernapercepçãodomodocomoserealizamosinvestimentos.Asociedadesofremudançasradicaise,talvez,deproporçõesmaioresdoqueasocorridasemséculospassados.Nãoexisteboladecristal,masalgoécerto:aperspectivaglobaldetransformaçõestranscendenossarealidadeimediata.Aconteçaoqueacontecer,sóexistemduasalternativas:segurançaouindependênciafinanceira.EoobjetivodePaiRico,PaiPobreéinstruiroleitoredespertarsuainteligênciafinanceiraeadeseusfilhos.

Compreagoraeleia

Page 441: Data Science do zero: Primeiras regras com o Python
Page 442: Data Science do zero: Primeiras regras com o Python

ONegóciodoSéculoXXIKiyosaki,RobertT.9788550804019160páginas

Compreagoraeleia

Vocêestáfuriosocomacorrupçãonomundoempresarial?Comosistemafinanceiroeosbancos?Comogoverno,quefazmuitopelascoisaserradasepoucopelascoisascertas?Ouestázangadoconsigomesmopornãoterconseguidocontrolarsuasfinançasantes?Avidaédura.Aquestãoé:Oquevocêestáfazendoarespeito?Reclamareresmungarsobreaeconomiaouresponsabilizarosoutrosnãosãoatitudesqueasseguramseufuturofinanceiro.Sevocêquiserriqueza,precisacriá-la.Vocêprecisaassumirocontroledeseufuturo,controlandosuafontederenda–hoje!Vocêprecisadeseupróprionegócio.Estespodemsertemposdifíceisparaamaioriadaspessoas,mas,paramuitosempresários,sãotemposplenosdepotencialeconômico.Nãoapenasagoraéahoradeteropróprionegócio,comonuncahouveumtempomelhor!

Compreagoraeleia