🤜 Next.js 14 Conf
ReactNext.js의 최신 버전, Next.js 14의 출시는 프론트엔드 개발의 세계에 새로운 변화의 바람을 불러일으키고 있습니다. 이 업데이트는 개발 속도와 사용자 경험을 극적으로 개선하는 몇 가지 중요한 기능을 제공합니다. 이 글에서는 Next.js 14의 핵심 기능과 그것들이 웹 앱 개발에 어떤 영향을 미칠지에 대해 이야기 해보겠습니다. Next.js 14의 혁신: 빠르고 효율적인 개발 경험 Next.js는 웹 앱 개발을 위한 최고의 프레임워크 중 하나로 자리 잡았으며, Next.js 14는 이러한 위치를 더욱 공고히 합니다. 이 버전에서는 특히 세 가지 주요 기능이 눈에 띕니다. 1. Turbopack: 속도와 효율성의 새로운 기준 Turbopack은 로컬 서버 시작 시간을 최대 53%까지 단축하고, 코드 업데이트 시간을 94%나 줄여줍니다. 이것은 대규모 애플리케이션에 대한 실제 벤치마크 결과로, Next.js의 성능 향상을 명확히 보여줍니다. 2. Server Actions: 간결하고 효율적인 백엔드 통합 Server Actions는 백엔드 엔드포인트 구축을 간소화하고, 사용자 경험을 향상시킵니다. 이를 통해 개발자는 API 경로를 수동으로 생성할 필요 없이, React 컴포넌트 내에서 직접 서버에서 실행되는 함수를 정의할 수 있습니다. 3. 부분적 Pre-rendering: 빠른 초기 응답과 동적 콘텐츠 Next.js의 새로운 부분적 pre-rendering 기능은 초기 정적 응답과 동적 콘텐츠 스트리밍을 조화롭게 결합합니다. 이는 사용자에게 더 빠른 페이지 로딩 경험을 제공하면서 동시에 동적인 콘텐츠를 유연하게 제공합니다. 개발의 편의성: 더 쉬워진 Next.js 개발 경험 Next.js 14는 개발자들이 보다 빠르고 효율적으로 작업할 수 있도록 설계되었습니다. 이러한 편의성은 다음과 같은 형태로 나타납니다: 통합된 캐싱 및 재검증: 데이터의 유효성을 빠르게 확인하고 캐시된 콘텐츠를 최신 상태로 유지합니다. 간단한 함수 호출: 서버 작업을 위한 복잡한 설정 없이 간단한 함수 호출로 백엔드 기능을 사용할 수 있습니다. 보다 효율적인 코드 작성: 개발자들은 코드 작성 시간을 줄이고, 더 직관적인 방식으로 애플리케이션을 구성할 수 있습니다. Server Action: 프론트엔드와 백엔드의 간편한 통합 Server Action은 백엔드 기능을 프론트엔드 컴포넌트에 직접 통합함으로써 개발자들이 더욱 직관적으로 작업할 수 있도록 해줍니다. 이 기능은 Next.js가 React Canary 채널의 안정성을 기반으로 하여 새로운 기능을 채택하는 방식을 반영합니다. 양식 제출 시 서버에서 실행되는 processFormData 함수를 직접 호출합니다. 이는 기존의 복잡한 API 경로 설정 없이 서버 로직을 구현할 수 있도록 해주는 혁신적인 방식입니다. Server Action의 장점 간결성: React 컴포넌트 내에서 서버 작업을 직접 정의함으로써 코드를 간결하게 유지할 수 있습니다. 타입 안전성: TypeScript를 사용하여 클라이언트와 서버 간 완벽한 타입 안전성을 보장합니다. 효율적인 데이터 처리: 단일 네트워크 왕복으로 데이터 변경, 페이지 리렌더링 또는 리다이렉션을 처리할 수 있습니다. 재사용성: 다양한 작업을 구성하고 재사용할 수 있어 개발 과정을 효율적으로 만듭니다. Server Action은 백엔드 개발 경험이 있는 개발자들에게 친숙하면서도, 웹의 기본 사항인 Form과 FormData Web API를 기반으로 새로운 접근 방식을 제공합니다. 이러한 방식은 개발자들이 Next.js를 사용하여 보다 신속하고 효과적으로 프로젝트를 진행할 수 있게 도와줍니다. 부분적 Pre-rendering Next.js를 위해 작업 중인 부분 pre-rendering(빠른 초기 정적 응답을 갖춘 동적 콘텐츠에 대한 컴파일러 최적화)의 미리 보기를 제공하고, pre-rendering은 서버측 렌더링(SSR), 정적 사이트 생성(SSG) 및 증분적 정적 재검증(ISR)에 대한 10년 간의 연구 개발을 기반으로 구축되었습니다. React Suspense를 기반으로 구축 부분 사전 렌더링은 Suspense 경계에 따라 정의됩니다. pre-rendering이 활성화되면 이 페이지는 boundary를 기반으로 정적 셸을 생성합니다. React Suspense의 은 사전 렌더링됩니다. 그런 다음 셸의 Suspense fallback은 cookie를 읽어 카트를 결정하거나 사용자를 기반으로 배너를 표시하는 등의 동적 컴포넌트로 대체됩니다. 요청이 이루어지면 정적 HTML 셸이 즉시 제공됩니다. 는 쿠키에서 읽어 사용자 session을 확인하므로 이 컴포넌트는 정적 셸과 동일한 HTTP 요청의 일부로 스트리밍됩니다. 추가 네트워크 왕복이 필요하지 않습니다. 가장 세부적인 정적 셸을 가지려면 Suspense boundary를 추가해야 할 수도 있습니다. 그러나 현재 loading.js를 이미 사용하고 있다면 이는 암시적인 Suspense boundray이므로 정적 셸을 생성하는 데 변경이 필요하지 않습니다. 메타데이터 개선 뷰포트, 색 구성표 및 테마에 대한 중요한 페이지 콘텐츠를 서버에서 스트리밍하기 전에 먼저 브라우저에 전송해야 하는 메타데이터가 있습니다. 이러한 태그가 초기 페이지 콘텐츠와 함께 전송되도록 하면 원활한 사용자 경험에 도움이 되며, 테마 색상을 변경하거나 뷰포트 변경으로 인해 레이아웃이 이동하여 페이지가 깜박이는 것을 방지할 수 있습니다. Next.js 14에서는 blocking 및 non-blocking 메타데이터를 분리했습니다. 메타데이터 옵션의 작은 하위 집합(small subset)만 차단되며, 우리는 non-blocking 메타데이터가 부분적으로 pre-rendering된 페이지가 정적 셸을 제공하는 것을 방해하지 않도록 하고 싶습니다. 다음 메타데이터 옵션은 이제 더 이상 사용되지 않으며 향후 주요 버전의 메타데이터에서 제거(deprecated)될 예정 이라고 합니다. : 뷰포트의 초기 확대/축소 및 기타 속성을 설정합니다. : 뷰포트의 지원 모드(밝음/어두움)를 설정합니다. : 뷰포트 주변의 크롬이 렌더링해야 하는 색상을 설정합니다. Next.js 14부터 이러한 옵션을 대체하는 새로운 옵션 및 가 있습니다. 다른 모든 옵션은 동일하게 유지됩니다. 마치며 turobopack과 pre-rendering 은 기대가 되는 부분이긴 하지만, ServerActions은 심플해 보이긴 하지만 이렇게 쓰면 코드가 지저분해 질것 같은데 이게 맞나 싶은 느낌이 들기도 하네요. 아무튼 프론트엔드의 변화 속도는 정말 빠른것 같다는 생각입니다.
2023년 12월 21일11분🌐 React Best Practice
React대세 프론트 라이브러리인 리엑트로 개발을 하기에 앞서 가급적이면 꼭 지켜야할 부분을들 정리하였습니다. 습관적으로 아래 내용들은 반드시 숙지합시다! 함수형 컴포넌트와 훅을 사용 React 함수 컴포넌트와 훅은 클래스에 비해 더 간결하고 읽기 쉬운 코드를 생성하므로 더 자주 사용해야 합니다. 상태사용 피하기 React 애플리케이션을 빌드할 때 상태를 많이 사용할수록 앱 전체에서 더 많은 데이터를 추적해야 하므로 상태를 최대한 사용하지 마십시오. 상태 사용을 최소화하는 한 가지 방법은 필요할 때만 선언하는 것입니다. 예를 들어 API에서 사용자 데이터를 가져오는 경우 개별 속성을 저장하는 대신 전체 사용자 개체를 상태에 저장합니다. 동일한 구성요소와 관련된 파일을 하나의 폴더에 정리 Navbar 구성 요소를 생성하는 경우 Navbar 구성 요소 자체, 스타일 시트, 구성 요소에 사용된 기타 JavaSript 및 자산 파일을 포함하는 navbar라는 폴더를 생성합니다. 인덱스 키를 Props Key로 사용하지 않기 아래와 같이 인덱스 키를 사용하면 때때로 작동하지만 인덱스를 키로 사용하면 특히 목록이 변경될 것으로 예상되는 경우 문제가 발생할 수 있습니다. 가능한 경우 div 대신 조각을 선택하십시오. 를 사용하면 DOM 크기가 증가합니다. 특히 태그 또는 DOM 노드가 많을수록 웹사이트에 더 많은 메모리가 필요하고 브라우저가 웹사이트를 로드하는 데 더 많은 전력을 사용하기 때문에 대규모 프로젝트에서 더욱 그렇습니다. 이로 인해 페이지 속도가 느려지고 잠재적으로 사용자 경험이 저하될 수 있습니다. 불필요한 태그를 제거하는 한 가지 예는 단일 요소를 반환할 때 태그를 사용하지 않는 것입니다. 명명 규칙 준수 구성 요소 이름을 지정할 때 항상 PascalCase를 사용하여 구성 요소가 아닌 다른 JSX 파일과 구별해야 합니다. (예: TextField, NavMenu 및 SuccessButton) handleInput() 또는 showElement()와 같은 React 구성 요소 내부에 선언된 함수에는 camelCase를 사용하십시오. 반복적인 코드 피하기 중복된 코드를 작성하고 있다는 것을 알게 되면 재사용할 수 있는 구성 요소로 변환하십시오. Props에 객체 구조화 사용 props 객체를 전달하는 대신 객체 구조화를 사용하여 props 이름을 전달하십시오. 이렇게 하면 사용할 때마다 props 객체를 참조할 필요가 없습니다. 맵을 사용하여 동적으로 배열 렌더링 map()을 사용하여 반복되는 HTML 블록을 동적으로 렌더링합니다. 예를 들어 map()을 사용하여 태그의 항목 목록을 렌더링할 수 있습니다. 각 React 구성 요소에 대한 테스트 작성 생성한 구성 요소에 대한 테스트를 작성하면 오류 가능성이 줄어듭니다. 테스트를 통해 구성 요소가 예상대로 작동하는지 확인합니다. React의 가장 일반적인 테스트 프레임워크 중 하나는 Jest이며 테스트를 실행할 수 있는 환경을 제공합니다. React Is a Powerful Tool, But You Must Follow Certain Practices React는 사용 방법 측면에서 다소 유연하지만 특정 사례를 따르면 경험을 최대한 활용하는 데 도움이 됩니다.
2022년 11월 24일6분📗 Angular Hybrid Rendering
Angular이전 글에서 CSR, SSR, SSG에 대해 각각의 차이점에 대해서 다뤄 보았습니다. Angular, React, Vue에서 각각의 방식으로 이러한 렌더링 방식으로 구현을 할 수가 있는데요. 대표적으로 Angular는 Universal, React는 NextJs, Vue는 Nuxt 등을 많이 사용하는 것 같네요. Angular는 국내에서 인기가 많이 없어서인지 해당 내용을 다루는 글이 없는듯하여 이번 기회에 제가 사용을 해보고 간단하게나마 정리해보겠습니다. 렌더링 종류에 대한 글은 여기를 참고 결론부터 말씀드리면 굉장히 쉽습니다! 먼저 간단하게 개념 정도만 다시 짚고 넘어가겠습니다. PreRendering 우리에게 가장 익숙한 클라이언트 렌더링(CSR) 방식은 잘 아실 테고, 서버사이드 렌더링(SSR)은 각 페이지에 대한 요청을 받으면 브라우저 엔진을 대신해서 서버에서 Node가 렌더링하고 결과물을 클라이언트에서 전달해주는 것이고, 사전 렌더링(PreRendering, SSG) 말 그대로 미리 렌더링을 해놓고 서버에서 해당 페이지에 대한 요청이 오면 미리 렌더링해두었던 페이지를 전달! 자 이제 누구도 알려주지 않았던, Angular Universal을 이용해 하이브리드 렌더링을 구현해 보겠습니다. Universal 이라는 새로운 프로젝트를 생성하고 universal package를 추가해줍니다. 을 추가 해주면 , 과 같은 뭔가 못보던 파일들이 생겨나는 것을 보실수 있습니다. 우리는 지금껏 브라우저 환경에서 클라이언트 렌더링을 사용했지만, 이제 브라우저 환경이 아닌곳에서 렌더링을 해야 하기 때문에 Node가 실행해줄 파일이 필요하기 때문입니다. 파일을 보면 처음 보는 scripts들도 확인 할 수 있습니다. 키값명칭을 보면 대충 어떤것을 하는것이겠구나.. 하는 정도의 감은 오셨을 겁니다. 먼저 을 해보기 위해 route 페이지가 필요한데, about와 contact 모듈을 추가하고 라우터에 등록 시켜보겠습니다. 각각의 컴포넌트를 생성해 router에 등록해도 되지만 아래와 같은 명령어를 통해 쉽게 추가할 수 있습니다. (Angular 프레임웍의 장점 이기도 하죠, 복잡해 보이지만 체계적이다? ^^;) Run 바로 사전 렌더링을 해보죠! !01 dist 디렉토리를 보시면 server, browser 두개의 디렉토리로 나누어져 있네요. server/main.js 이 파일이 node에서 실행해주는 파일이 되는것이고요, browser 디렉토리 내부에 있는 파일들이 렌더링된 파일들입니다. 그런데 index.html 파일만 있는게 아니고 router에 등록한 about, contact의 index.html파일도 각각의 디렉토리에 생성이 되었습니다. 각각의 페이지 요청이 오면 미리 랜더링해둔 index.html파일을 전달하기 위함이죠. Options 그리고 파일의 옵션정보를 이용해서 원하는 페이지만 사전 렌더링을 해둘수가 있습니다. 예를 들어 about 페이지만 사전 렌더링을 해두고 싶다. 그럼 , 배열에 렌더링할 페이지만 추가해줍니다. projects > hybrid-rendering-app > architect > prerender "routesFile": "./routes.txt" 파일에 경로들을 설정할수도 있습니다. 더 자세한 정보는 여기에서 확인해 주세요 이제 node로 해당 js파일을 실행해주면 됩니다. Server Start Node Express server listening on http://localhost:4000 서버를 구동해주고 브라우저에 접속해서 Network 탭을 살펴 보면 각 라우팅에 접근할 때마다 해당 문서를 다운받는 것을 확인할 수 있습니다. !main !about !contact 최초 접속시 index.html 파일을 받아 그안에서 javascript 파일에 의해 동적으로 문서를 변경하는 SPA방식과는 달리 라우터 접근시 사전에 렌더링된 문서를 서버에서 전달해주게 됩니다. 거의 변경이 없는 페이지들의 경우 이렇게 사전에 렌더링을 해두면 성능적인 부분에서 매우 효휼적일 것이지만, 페이지 내용이 자주 변경이 된다거나 게시판 같은 동적인 라우팅을 사용한다면, 에 렌더링할 페이지만 명시해주면 선택적으로 사전 렌더링을 할 수 있게 되는 것 입니다.
2022년 10월 27일7분🎨 개발자가 잘 모르는 HTML 태그
Javascript웹을 개발하다보면 익숙한 HTML 태그들만 사용하게 되고 그외 기능적인 부분들은 javascript나 css로 구현을 하게 되는데 (div, span anchor, header, list.. 등) 아주 유용하면서 개발자들이 잘 모르는 태그들을 한번 살펴보도록 하겠습니다. CSS ,JS로만 가능하다고 생각한 것을 이제 HTML로 심플하게 개발해 보세요. progress, meter !progress !meter > 는 속성에 따라 값이 높고 낮을때 색상을 다르게 설정하여 작업의 진행상태를 표현할 수 있다. 미세먼지 상태와 같은 좋음, 나쁨, 매우나쁨 등의 상태를 함께 표현하기에 적합합니다. details, summary 과 같은 유저의 클릭으로 정보를 보여주고 숨기는 패턴 적용이 가능합니다. !details 달력, 날짜 선택기 input 태그의 type으로 설정이 가능합니다. 단 브라우저마다 UI가 가르게 표현됩니다. picture 유저의 장치나 환경에 따라 각기 다른 버전의 이미지를 표시가허나 브라우저가 이미지 포맷을 지원하지 않을 때 다른 포맷을 제공할 수 있습니다. > 💡 환경에 맞는 이미지를 다운로드 해서 보여줄 수 있으므로, 페이지 로딩 속도를 높일 수 있음. datalist javascript 없이 자동완성 기능을 만들수 있고 필터 기능까지 제공 합니다. >주의사항 input의 list와 datalist의 id는 동일해야됨 !datalist 마치며 유용하고 간단하게 사용할 수 있는 HTML태그 이니 꼭 기억해서 활요하면 좋을 것 같습니다. (출처 : https://www.youtube.com/watch?v=EMOlLLTAZMs)
2022년 01월 16일3분🔮 Server/Client Side Rendering
React!01 웹 어플리케이션의 렌더링 방식은 크게 서버와 클라이언트 렌더링 방식이 있습니다. 이 방식을 살펴보기에 앞서 과거 웹 사이트의 역사에 대해 간단하게 살펴보겠습니다. Static Site 1990년 중반까지는 대부분의 사이트가 static 사이트 였습니다. 서버에 이미 잘 만들어진 html문서들이 있고 사용자가 부라우저에서 주소에 접속하면 서버에 이미 배포되어있는 HTML 문서를 받아와 보여주는 형식이죠. 이런한 방식의 문제점은 페이지에서 다른 메뉴를 클릭하면 다시 서버에서 해당페이지의 HTML을 받아와서 페이지 전체가 업데이트 되어야 합니다. (깜박, 깜박... 정말 사용성이 떨어지죠) iframe 이후 1996년 문서내에서 또 다른 문서를 담을 수 있는 태그가 도입이 되었고 이제는 페이지내에서 부분적으로 문서를 받아와서 업데이트 할 수가 있게 됩니다. (지금도 간혹 쓰이고 있는 태그입니다.) XMLHttpRequest API 그리고 1998년 우리가 많이 쓰고 있는 fetch API의 원조 가 개발이 되어 이제는 HTML문서 전체가 아니라 과 같은 포멧으로 서버에서 가법게 필요한 데이터만 받아 올 수 있게 됩니다. 그 데이터를 자바스크립트를 이용해서 동적으로 HTML요소를 생성하여 페이지에 업데이트 하는 방식이죠. AJAX, SPA 2005년 이런 방식이 공식적으로 AJAX라는 이름을 가지게 되고 구글에서도 AJAX를 이용해 GMail, Google Map과 같이 우리가 많이 쓰고 있는 웹 어플리케이션을 만들기 시작합니다. 이것이 바로 현재 널리 쓰이고 있는 입니다. 사용자가 한 페이지 내에서 머무르면서 필요한 데이터를 서버에서 받아와 부분적으로만 업데이트 하게 되죠. 이런 방식으로 하나의 어플리케이션을 사용 하듯 웹사이트에서도 이제 사용성이 굉장히 좋아지게 됩니다. 이런 SPA트랜드 + 사용자의 PC성능이 점차 좋아져서 많은 것들을 무리 없이 처리할 수 있게 되었고 자바스크립트도 표준화가 잘 되어짐에 따라 Angular, React, Vue와 같은 프레임워크가 나와 시대로 접어듭니다. Client Side Rendering 클라이언트 사이드 렌더링이란 쉽게 얘기하면 클라이언트에서 다 처리하는걸 말하는데요, 서버에서 index.html 파일을 클라이언트에 보내주면 자바스크립트를 이용해 서버에서 데이터를 요청하고 받아온 데이터를 기반으로 동적으로 HTML을 생성하여 사용자에게 최종적인 어플리케이션을 보여 주게 됩니다. 그런데 말입니다. 이런 클라이언트 사이드 렌더링은 다음과 같은 문제점이 있습니다. 사용자가 첫 화면을 보기까지의 시간이 오래 걸릴 수 있다 썩 좋지 않는 SEO(Search Engin Optimization) Server Side Rendering 이러한 CSR의 문제점 때문에 기존 Static 사이트에서 영감을 받은 이 도입되게 됩니다. 클라이언트에서 모든 것을 처리하는 방식과는 다르게 웹사이트에 접속하면 서버에서 필요한 데이터를 모두 가져와서 HTML파일을 만들게 되고 이렇게 만들어진 HTML파일을 동적으로 제어할 수 있는 소스코드와 함께 클라이언트에게 보내줍니다. 그럼 클라이언트에서는 잘 만들어진 HTML문서를 받아와 바로 사용자에게 보여줄 수 있게 되는거죠. 어떤것이 더 좋을까? 이런 SSR을 이용하게 되면 CSR과 비교했을때 다음과 같은 장점이 있습니다. 첫 번째 페이지 로딩이 빨라짐 모든 컨텐츠가 HTML에 담겨져 있기 때문에 효율적인 SEO 가능 그럼 이런 SSR 과연 모든것의 해결책이 될까요? 그것은 아닙니다. SSR에도 다음과 같은 큰 문제점이 존재하는데요, Static 사이트와 같은 깜박임 이슈 즉, 썩 좋지 않는 UX 서버의 과부화 (사용자가 많은 사이트 일수록) 사용자가 빠르게 웹사이트를 확인할 수 있지만 동적으로 데이터를 처리하는 자바스크립트를 다운로드 받지 못해 여기저기 클릭해도 반응이 없는 경우가 발생할 수 있습니다. TTV(Time To View), TTI(Time to interact) TTV, TTI 두가지에 대해 좀 더 살펴보겠습니다. CSR은 1. 사이트에 접속 2. 서버에게서 텅텅빈 인덱스 파일을 받아옴 3. 웹사이트에 필요한 자바스크립트를 서버에 요청 4. 서버에게서 자바스크립트 파일을 받아옴 5. 최종적으로 동적으로 HTML을 생성할 수 있는 자바스크립트를 받아와 웹사이트를 사용자가 볼수 있게 해줌과 동시에 사용자 클릭이 가능 >즉, CSR은 TTV 사용자가 웹사이트를 볼 수 있음과 동시에 TTI 클릭을 하거나 인터랙션이 가능하게 됩니다. 반대로 SSR은 1. 사이트에 접속을 하면 서버에서 잘 만들어진 인덱스 파일을 받아오고 2. 사용자는 웹사이트를 볼수 있게 됩니다. 3. 하지만 아직 동적으로 제어할 수 있는 자바스크립트 파일은 받아오지 않았기 때문에 이 순간 사용자가 클릭을 해도 반응이 없습니다. 4. 이후 최종적으로 자바스크립트 파일을 서버에서 받아온 이후에나 사용자의 클릭을 처리할 수 있는 인터랙션이 가능해집니다. >즉, SSR은 사용자가 사이트를 볼 수 있는 시간과 실제로 인터렉션을 할 수 있는 공백 기간이 꽤 긴편입니다. 그래서 웹사이트의 성능을 분석할때 와 도 중요한 매트릭으로 사용할 수 있습니다. CSR을 많이 사용하는 분들이라면 우리가 최종적으로 번들링해서 사용자에게 보내주는 자바스크립트 파일을 어떻게 하면 효율적으로 많이 분할해서 첫번째로 사용자가 보기위한 정말 필수적인 부분만 보낼 수 있을지를 고민해야 합니다. SSR같은 경우는 사용자가 보고, 인터렉션 하는 시간의 공백을 줄이기 위해 어떻게 해야할지, 어떻게 조검더 매끄러운 UI와 UX를 제공할 수 있을지 고민해 보면 좋을것 같습니다. 마치며 요즘에는 꼭 CSR 또는 SSR만을 고집해서 사용하기 보다는 와 같은 도 있습니다. (지금 보고 계시는 블로그도 Gatsby를 사용) 뿐만아니라 React의 Next.js, Angular Universal과 같은 Angular 애플리케이션을 서버에서 실행하는 테크닉도 있으니, 어떤 방식이 좋고 나쁘고가 아닌 각각의 방식을 이해하고 필요에 의해 선택적으로 사용하면 될 것 같습니다.
2022년 01월 08일10분📗 Angular Google Analytics Traking
Angular구글 분석을 사용하기 위해서는 추척아이디가 포함된 스크립트를 index.html 페이지에 삽입만 하면 되기 때문에 아주 간단하게 연동을 할 수 있습니다. 그러나 Angular와 같은 SPA방식은 단일페이지로 되어 있기 때문에 단순히 스크립트를 복사 붙여넣기 방식으로 만으로 우리가 원하는 분석데이터를 수집하기에는 아쉬움이 있습니다. 그럼 어떻게? Angular는 단일페이지 애플리케이션 프레임워크입니다. 즉, 브라우저는 기술적으로 한 페이지만 로드하고 페이지가 변경되면 DOM은 프레임워크의 JavaScript 코드에 지정된 대로 페이지의 일부만 업데이트 합니다. 컴포넌트는 시각적인 퍼즐의 일부를 구성하는 것이고 특정 보기에서 다른 곳으로 이동할 수 있고 주소 표시줄의 링크가 일치하도록 동적으로 변경되지만 여전히 동일한 루트 페이지에 있습니다. 브라우저가 페이지를 아동해도 새로고침 없이 여전히 동일한 페이지 이기 때문에 Google Analytics 태크의 기본 구현은 이를 알지 못합니다. 따라서 라우터 이벤트를 통해 변경된 페이지 정보를 추가로 전달해 주어야 합니다. gtag.js Google Analytics를 통한 추적에는 및 의 두 가지 버전이 있습니다. ga는 더 오래된 버전이며 Google에서는 대신 gtag.js 구현을 추진하고 있습니다. 시작하려면 Google Analytics 패널로 이동하여 복사하여 붙여넣기 코드를 찾아야 합니다. 트래킹ID가 포함된 코드를 복사하여 섹션에 넣어주면 됩니다. 데이터 스트림에서 스트림을 추가하면 그림과 같이 트래킹ID 및 HTML에 삽입할 script를 확인 할 수 있습니다. !01 Router Events 정보 수집 이제 페이지 이동 정보를 수집하기 위해 별도의 코드를 작성해야 합니다. Angular에는 특정 정보에 액세스하는 데 사용할 수 있는 몇 가지 이벤트 처리 방법이 있는데, 우리는 사용자가 탐색한 URL정보가 필요하기에 라우터 이벤트를 구독 할 것입니다. 이 정보를 추출하기 위해 메소드를 사용하여 라는 속성에 액세스할 수 있습니다. 이 속성에는 라우팅 URL의 부분이 포함됩니다. 코드 효율성을 위해 이 모든 것을 파일에 배치할 수 있습니다. 이 파일은 모든 것의 최상위 수준에서 로드되는 첫 번째 파일이기 때문입니다. 추적을 더욱 강화하는 데 사용할 수 있는 다른 매개변수가 있습니다. 최신 매개변수 목록은 Google Analytics 추적 페이지 보기 문서 페이지에서 확인할 수 있습니다. (https://developers.google.com/analytics/devguides/collection/gtagjs/pages) Event Tracking 이벤트 추적은 Analytics 통계에 또 다른 데이터 계층을 추가합니다. 이벤트 트래킹은 원하는 이벤트가 발생시 수집될 수 있도록 해야 하기 때문에 Angular 서비스를 만들고 구성 요소에서 사용할 것입니다. 이렇게 하려면 CLI를 사용하여 서비스를 생성한 다음 이를 공급자 중 하나로 파일에 추가할 수 있습니다. 새로 생성된 서비스 파일 내에서 호출될 때 본질적으로 실행하고 형식이 정확하고 에서 요구하는 방식인지 확인하는 함수를 생성할 것입니다. 또한 외부에서 로드된 라이브러리에 서비스를 노출하려면 를 Function으로 선언해야 합니다. 이 서비스를 사용하려면 컴포넌트로 가져와 버튼 클릭 이벤트와 같은 일이 발생할 때 트래킹 하려는 값을 에 전달하여 실행해야 합니다. 그런 다음 를 처리할 구성 요소에서 를 호출할 수 있습니다. 작동하려면 생성한 서비스를 구성 요소로 가져와야 합니다. 나만의 이벤트 및 이벤트 카테고리를 만들 수 있습니다. Google에는 사용할 수 있도록 미리 정의된 목록이 있습니다. 전체 목록은 여기에서 찾을 수 있습니다.(https://developers.google.com/analytics/devguides/collection/gtagjs/events) 마치며 단일 페이지 및 프로그레시브 웹 응용 프로그램에서 구현하는 것은 약간의 코드만 추가하면 되기 때문에 어려운 부분은 없습니다. 와 Angular 라우팅의 작동 방식을 이해하고 Analytics 추적을 구현하는 것이 중요합니다. 패키지를 설치하여 사용하는 방법도 있으니 참고 하면 좋을 것 같습니다. (https://www.npmjs.com/package/angular-google-tag-manager)
2022년 01월 02일8분🤠 Web Storage
ReactWeb Storage Web Storage는 HTML5에서 추가된 간단한 키와 값을 저장(key-value storage) 할 수 있는 저장소이다. 데이터의 지속성에 따라 영구저장소(LocalStorage)와 임시저장소(SessionStorage) 두가지를 지원한다. 그 동안 많이 사용해 왔던 쿠키와 거의 차이가 없어 보이지만 몇 가지 쿠키의 단점을 극복하는 개선점이 도입이 되었다. Web Storage 특징 Web Storage는 다음과 같은 특징이 있다. 키와 값은 무조건 문자열로 변환되어 저장된다. (객체를 읽고 쓰려면 JSON.stringify, JSON.parse를 사용) 브라우저별 용량제한이 다르다. (용량 제한은 대략 5MB) 도메인별 Storage는 다르며 도메인별로 용량 제한이 있다. (Protocal, host, port가 같으면 같은 스토리지를 공유) 이것 마저 용량이 부족하다면 indexedDB가 있다. (indexedDB에 대해서는 다음에 좀더 자세히 알아보도록 하고,,) localStorage와 sessionStorage Web Storage는 데이터의 지속성에 따라 두 가지 용도의 저장소를 제공한다. LocalStorage 저장한 데이터를 명시적으로 지우지 않는 이상 영구적으로 보관이 가능하다. 앞서 말한 것처럼 도메인별로 스토리지가 생성이 되고 Windows 전역 객체의 LocalStorage라는 컬렉션을 통해 저장과 조회가 이루어진다. SessionStorage SessionStorage는 데이터가 지속적으로 보관이 되지 않는다. 이는 마치 브라우저 기반 세션 쿠키와 그 성질이 비슷한데, 현재 페이지가 브라우징되고 있는 브라우저 컨텍스트 내에서만 데이터가 유지된다. 쉽게 말하자면, 탭 브라우징이나 브라우저를 하나 더 실행해서 같은 페이지를 실행했을 때, 이 두 페이지의 SessionStorage는 각각 별개의 영역으로 서로 침범하지 못한다는 의미이다. 이는 도메인만 같으면 전역적으로 공유 가능한 LocalStorage와 구분 되는 가장 큰 특징이다. Cookie 후속 요청으로 서버로 다시 보내야하는 데이터를 저장한다. 만료는 유형에 따라 다르며 만료 기간은 서버 측 또는 클라이언트 측 (일반적으로 서버 측)에서 설정할 수 있다. 쿠키는 주로 서버 측에서 읽기(클라이언트 측에서 읽을 수도 있음) 위한 것이며, Local Storage 및 Session Storage는 클라이언트 측에서만 읽을 수 있다. 크기는 4KB보다 작아야 한다. 해당 쿠키에 대해 httpOnly 플래그를 true로 설정하여 쿠키를 안전하게 만들 수 있다. 이렇게하면 쿠키에 대한 클라이언트 측 액세스가 차단된다. 각 저장소별로 데이터가 어떻게 저장되고 있는지를 보고 싶다면 디버깅모드(F12) > Application에서 확인 할 수 있다. !01 마치며... WebStorage의 보안은 서로 다른 도메인의 데이터 침범을 막고는 있지만 클라이언트, 즉 사용자를 막고 있지는 않다. 클라이언트는 얼마든지 저장된 값을 임의로 수정이 가능하다. 이것은 쿠키와 동일한 개념이다. 그렇다고 쿠키에 비해 별다른 보안 취약점을 더 가진 것은 아니다. 따라서 개발자는 사용자에 의한 이러한 임의 변경에 항상 예의 주시하고 방어 코드의 작성을 잊지 말아야 한다.
2021년 04월 11일5분Python 병렬 처리를 위한 Dask
Python큰 데이터를 처리하기 위해서는 병렬처리가 효율적인데, 그래서 Apache Spark의 pyspark를 많이들 사용한다. 속도는 매우 빠르지만 Pandas에 익숙한 사람들은 불편한 점이 있다. 이를 해소시켜 줄만한 라이브러리가 바로 Dask이다. (그 외 modin, ray, vaex등이 있는듯 하니 관심있다면 찾아보자.) 병렬 컴퓨팅(Parallel Computing)이란? 여러 단계 별로 구성된 작업을 수행할 때 각 단계에서 이전 작업이 완료되기를 기다리지 않고 이들이 동시에 처리되도록 하는 것이다. 따라서 난이도는 각 작업의 특징에 따라 달라지는데, 각 작업의 시작과 끝을 제외하고 각 단계가 서로 독립적으로 수행되는 작업은 병렬화가 쉽고, 각 단계 사이의 많은 데이터가 전송되고 교환되어야 하는 작업은 병렬화가 어렵다. Dask 란? Dask는 Python 라이브러리로 기능면에서는 Apache Spark와 비슷하지만 numpy, pandas와 긴밀하게 연결되어 있어서 python 사용자들이 Spark보다 훨씬 쉽게 배우고 활용할 수 있고, 다임과 같은 두 가지 기능을 가진다. 1. 가상 데이터프레임 2. 병렬처리용 작업 스케줄러 가상 데이터프레임 가상 데이터프레임은 Pandas 데이터프레임과 비슷한 기능을 제공하지만 실제로 모든 데이터가 메모리 상에 로드되어 있는 것이 아니라 하나 이상의 파일 혹인 데이터베이스에 존재하는 채로 처리할 수 있는 기능이다. 따라서 메모리 크기와 관계 없이 엄청나게 큰 CSV 파일을 가상 데이터프레임으로 로드하거나 같은 형식의 데이터를 가진 여러개의 CSV 파일을 하나의 가상 데이터프레임에 로드할 수 있다. 실제로 Dask의 가상 데이터프레임을 어떻게 쓸 수 있는지 알아보기 위해 간단한 CSV 파일을 만들어 살펴보자. Writing data1.csv Dask 패키지가 설치 되어 있지 않다면 설치를 하고 해당 패키지를 임포트하자. Install conda install dask (anaconda environment) pip install "dask[complete]" (pip based installation) Dask DataFrame Structure: | | time | temperature | humidity | |----------------|-------|-------------|----------| | npartitions=1 | | | | | dtype | int64 | int64 | int64 | | ... | ... | ... | ... | Dask Name: , 1 tasks 사용법을 보면 기존에 Pandas 데이터프레임과 유사하지만 실제 결과를 보면 아직 데이터를 메모리에 읽지 않았기 때문에 값은 표시되지 않는다. 값을 실제로 표시하려면 , 명령을 내리면 해당 데이터를 읽어서 표시한다. | | time | temperature | humidity | |---|------|-------------|----------| | 0 | 0 | 22 | 58 | | 1 | 1 | 21 | 57 | | 2 | 2 | 25 | 57 | | 3 | 3 | 26 | 55 | | 4 | 4 | 22 | 53 | 위의 경우 temperature 열의 평균을 구하기 위해 명령 입력하였지만 결과가 나오지 않는다. 그 이유는 는 연산 반환값의 결과가 아닌 작업(task)이기 때문이다. 구체적으로 어떤 작업인지를 보려면 매서드를 사용하여 작업 그래프(graph)를 볼 수 있다. 작업 그래프란 이 계산을 하기 위해 실제로 CPU가 해야할 일들의 순서도라고 생각하면 된다. 이 작업의 값을 실제로 구하려면 결과로 받은 작업 객체의 매서드를 호출해야 한다. 23.166666666666668 이번에는 이 값을 화씨로 변환해 보자 0 71.6 1 69.8 2 77.0 3 78.8 4 71.6 5 73.4 Name: temperature, dtype: float64 이번에는 이 값으로 원래의 temperature 열을 갱신해보자. 이 때는 Pandas의 문법을 쓰지 못하고 다음과 같이 메서드를 사용해야 한다. 메서드를 사용할 때는 를 할 필요가 없다. | | time | temperature | humidity | |---|------|-------------|----------| | 0 | 0 | 71.6 | 58 | | 1 | 1 | 69.8 | 57 | | 2 | 2 | 77.0 | 57 | | 3 | 3 | 78.8 | 55 | | 4 | 4 | 71.6 | 53 | 자료형을 변환하거나 새로운 열을 추가하는 것도 가능하다. | | time | temperature | humidity | title | |---|------|-------------|----------|-----------------| | 0 | 0 | 71.6 | 58 | 71.6 degree | | 1 | 1 | 69.8 | 57 | 69.8 degree | | 2 | 2 | 77.0 | 57 | 77.0 degree | | 3 | 3 | 78.8 | 55 | 78.8 degree | | 4 | 4 | 71.6 | 53 | 71.6 degree | Apache Spark를 사용해본 사람이라면 Transformation과 Action의 개념과 유사하다는 것을 눈치 챘을것이다! 복수 데이터에 대한 가상 데이터프레임 Dask는 가상 데이터프레임으로 원천 데이터 파일을 하나가 아닌 복수로 설정할 수도 있다. 예를 들어 앞서 보았던 파일 이외에도 다음과 같이 , 파일이 있을 경우, 이 파일을 한 번에 하나의 데이터프레임으로 읽어들일 수도 있다. Writing data2.csv Writing data3.csv 복수 파일은 와일드카드(*) 기호를 이용하여 읽는다. time 18 temperature 18 humidity 18 dtype: int64 count 18.000000 mean 23.166667 std 1.823055 min 21.000000 25% 22.000000 50% 22.500000 75% 24.500000 max 26.000000 Name: temperature, dtype: float64 대량 데이터의 병렬 처리 자 이제 Dask로 대량의 데이터를 처리해보자. 샘플로 쓸 데이터는 미국 정부가 발표하는 공개 정보 중 하나로 시카고의 범죄 관련 데이터이다. https://catalog.data.gov/dataset/crimes-2001-to-present-398a4 다음 명령으로 이 데이터를 다운로드 받을 수 있다. CSV 파일의 크기가 1.3GB가 넘으므로 다운로드에 10분 이상 걸릴수 있다. (만약 윈도우라면 wget을 사용하기 위해 https://eternallybored.org/misc/wget/ 여기에서 wget 최신 버전을 받고 c:\windows\system32에 위치시키면 된다.) ...... .......... .......... .......... 291K 1218900K .......... .......... .......... .......... .......... 11.7M 1218950K .......... .......... .......... .......... .......... 9.51M 1219000K .......... .......... .......... .......... .......... 14.1M 1219050K .......... .......... .......... .......... .......... 12.5M 1219100K .......... .......... .......... .......... .......... 10.1M 1219150K .......... .......... .......... .......... .......... 292K 1219200K .......... .......... .......... .......... .......... 9.68M 1219250K .......... .......... .......... .......... .......... 11.6M 1219300K .......... .......... .......... .......... .......... 12.1M 1219350K .......... .......... .......... .......... .......... 11.0M 1219400K .......... .......... .......... .......... .......... 10.7M 1219450K .......... .......... .......... .......... .......... 293K 1219500K .......... .......... .......... .......... .......... 11.5M 1219550K .......... .......... .......... .......... .......... 11.2M 1219600K .......... .......... .......... .......... .......... 9.82M 1219650K .......... .......... .......... .......... .......... 11.7M 1219700K .......... .......... .......... .......... .......... 11.9M 1219750K .......... .......... .......... .......... .......... 238K 1219800K .......... .......... .......... .......... .......... 11.3M 1219850K .......... .......... .......... .......... .......... 11.7M 1219900K .......... .......... .......... .......... .......... 12.0M 1219950K .......... .......... .......... .......... .......... 11.7M 1220000K .......... .......... .......... .......... .......... 9.24M 1220050K .......... .......... .......... .......... .......... 293K 1220100K .......... .......... .......... .......... .......... 11.9M 1220150K .......... .......... .......... .......... .......... 12.2M 1220200K .......... .......... .......... .......... .......... 11.2M 1220250K .......... .......... .......... .......... .......... 11.3M 1220300K .......... .......... .......... .......... .......... 12.1M 1220350K .......... .......... .......... .......... .......... 292K 1220400K .......... .......... .......... .......... .......... 11.8M 1220450K .......... .......... .......... .......... .......... 11.6M 1220500K .......... .......... .......... .......... .......... 9.90M 1220550K .......... .......... .......... .......... .......... 8.73M 1220600K .......... .......... .......... .......... .......... 12.4M 1220650K .......... .......... .......... .......... .......... 3.45M 1220700K .......... .......... .......... .......... .......... 315K 1220750K .......... .......... .......... .......... .......... 12.7M 1220800K .......... .......... .......... .......... .......... 9.12M 1220850K .......... .......... .......... .......... .......... 9.26M 1220900K .......... .......... .......... .......... .......... 11.2M 1220950K .......... .......... .......... .......... .......... 12.1M 1221000K .......... .......... .......... .......... .......... 296K 1221050K .......... .......... .......... .......... .......... 13.0M 1221100K .......... .......... .......... .......... .......... 12.3M 1221150K .......... .......... .......... .......... .......... 10.1M 1221200K .......... .......... .......... .......... .......... 8.55M 1221250K .......... .......... .......... .......... .......... 11.6M 1221300K .......... .......... .......... .......... .......... 249K 1221350K .......... .......... .......... .......... .......... 12.1M 1221400K .......... .......... .......... .......... .......... 10.8M 1221450K .......... .......... .......... .......... .......... 10.7M 1221500K .......... .......... .......... .......... .......... 13.7M 1221550K .......... .......... .......... .......... .......... 11.8M 1221600K .......... .......... .......... .......... .......... 665K 1221650K .......... .......... .......... .......... .......... 502K 1221700K .......... .......... .......... .......... .......... 12.0M 1221750K .......... .......... .......... .......... .......... 9.97M 1221800K .......... .......... .......... .......... .......... 13.0M 1221850K .......... .......... .......... .......... .......... 11.0M 1221900K .......... .......... .......... .......... .......... 12.7M 1221950K .......... .......... .......... .......... .......... 293K 1222000K .......... .......... .......... .......... .......... 12.0M 1222050K .......... .......... .......... .......... .......... 11.6M 1222100K .......... .......... .......... .......... .......... 11.7M 1222150K .......... .......... .......... .......... .......... 12.0M 1222200K .......... .......... .......... .......... .......... 11.1M 1222250K .......... .......... .......... .......... .......... 1.50M 1222300K .......... .......... .......... .......... .......... 355K 1222350K .......... .......... .......... .......... .......... 10.3M 1222400K .......... .......... .......... .......... .......... 8.38M 1222450K .......... .......... .......... .......... .......... 11.3M 1222500K .......... .......... .......... .......... .......... 12.0M 1222550K .......... .......... .......... .......... .......... 10.6M 1222600K .......... .......... .......... .......... .......... 297K 1222650K .......... .......... .......... .......... .......... 14.1M 1222700K .......... .......... .......... .......... .......... 9.30M 1222750K .......... .......... .......... .......... .......... 11.5M 1222800K .......... .......... .......... .......... .......... 9.04M 1222850K .......... .......... .......... .......... .......... 11.0M 1222900K .......... .......... .......... .......... .......... 685K 1222950K .......... .......... .......... .......... .......... 376K 1223000K .......... .......... .......... .......... .......... 10.8M 1223050K .......... .......... .......... .......... .......... 11.9M 1223100K .......... .......... .......... .......... .......... 11.5M 1223150K .......... .......... .......... .......... .......... 12.4M 1223200K .......... .......... .......... .......... .......... 9.14M 1223250K .......... .......... .......... .......... .......... 296K 1223300K .......... .......... .......... .......... .......... 11.7M 1223350K .......... .......... .......... .......... .......... 12.1M 1223400K .......... .......... .......... .......... .......... 10.1M 1223450K .......... .......... .......... .......... .......... 13.1M 1223500K .......... .......... .......... .......... .......... 11.4M 1223550K .......... .......... .......... .......... .......... 12.3M 1223600K .......... .......... .......... .......... .......... 294K 1223650K .......... .......... .......... .......... .......... 12.2M 1223700K .......... .......... .......... .......... .......... 11.6M 1223750K .......... .......... .......... .......... .......... 11.6M 1223800K .......... .......... .......... .......... .......... 10.2M 1223850K .......... .......... .......... .......... .......... 12.9M 1223900K .......... .......... .......... .......... .......... 294K 1223950K .......... .......... .......... .......... .......... 16.0M 1224000K .......... .......... .......... .......... .......... 9.55M 1224050K .......... .......... .......... .......... .......... 11.6M 1224100K .......... .......... .......... .......... .......... 11.2M 1224150K .......... .......... .......... .......... .......... 11.9M 1224200K .......... .......... .......... .......... .......... 1.24M 1224250K .......... .......... .......... .......... .......... 372K 1224300K .......... .......... .......... .......... .......... 13.5M 1224350K .......... .......... .......... .......... .......... 12.1M 1224400K .......... .......... .......... .......... .......... 8.88M 1224450K .......... .......... .......... .......... .......... 11.2M 1224500K .......... .......... .......... .......... .......... 11.7M 1224550K .......... .......... .......... .......... .......... 244K 1224600K .......... .......... .......... .......... .......... 11.3M 1224650K .......... .......... .......... .......... .......... 10.3M 1224700K .......... .......... .......... .......... .......... 13.8M 1224750K .......... .......... .......... .......... .......... 12.3M 1224800K .......... .......... .......... .......... .......... 9.29M 1224850K .......... .......... .......... .......... .......... 11.6M 1224900K .......... .......... .......... .......... .......... 296K 1224950K .......... .......... .......... .......... .......... 7.57M 1225000K .......... .......... .......... .......... .......... 11.0M 1225050K .......... .......... .......... .......... .......... 14.8M 1225100K .......... .......... .......... .......... .......... 11.1M 1225150K .......... .......... .......... .......... .......... 12.4M 1225200K .......... .......... .......... .......... .......... 297K 1225250K .......... .......... .......... .......... .......... 11.7M 1225300K .......... .......... .......... .......... .......... 8.52M 1225350K .......... .......... .......... .......... .......... 11.6M 1225400K .......... .......... .......... .......... .......... 11.4M 1225450K .......... .......... .......... .......... .......... 8.07M 1225500K .......... .......... .......... .......... .......... 11.7M 1225550K .......... .......... .......... .......... .......... 301K 1225600K .......... .......... .......... .......... .......... 7.09M 1225650K .......... .......... .......... .......... .......... 14.4M 1225700K .......... .......... .......... .......... .......... 9.71M 1225750K .......... .......... .......... .......... .......... 9.20M 1225800K .......... .......... .......... .......... .......... 11.6M 1225850K .......... .......... .......... .......... .......... 302K 1225900K .......... .......... .......... .......... .......... 10.3M 1225950K .......... .......... .......... .......... .......... 9.68M 1226000K .......... .......... .......... .......... .......... 9.45M 1226050K .......... .......... .......... .......... .......... 11.7M 1226100K .......... .......... .......... .......... .......... 8.02M 1226150K .......... .......... .......... .......... .......... 11.8M 1226200K .......... .......... .......... .......... .......... 247K 1226250K .......... .......... .......... .......... .......... 12.0M 1226300K .......... .......... .......... .......... .......... 11.7M 1226350K .......... .......... .......... .......... .......... 11.3M 1226400K .......... .......... .......... .......... .......... 9.61M 1226450K .......... .......... .......... .......... .......... 12.1M 1226500K .......... .......... .......... .......... .......... 295K 1226550K .......... .......... .......... .......... .......... 13.4M 1226600K .......... .......... .......... .......... .......... 10.9M 1226650K .......... .......... .......... .......... .......... 12.1M 1226700K .......... .......... .......... .......... .......... 9.85M 1226750K .......... .......... .......... .......... .......... 14.1M 1226800K .......... .......... .......... .......... .......... 2.31M 1226850K .......... .......... .......... .......... .......... 327K 1226900K .......... .......... .......... .......... .......... 11.7M 1226950K .......... .......... .......... .......... .......... 11.4M 1227000K .......... .......... .......... .......... .......... 11.1M 1227050K .......... .......... .......... .......... .......... 9.91M 1227100K .......... .......... .......... .......... .......... 13.9M 1227150K .......... .......... .......... .......... .......... 296K 1227200K .......... .......... .......... .......... .......... 8.93M 1227250K .......... .......... .......... .......... .......... 12.0M 1227300K .......... .......... .......... .......... .......... 11.2M 1227350K .......... .......... .......... .......... .......... 12.1M 1227400K .......... .......... .......... .......... .......... 11.2M 1227450K .......... .......... .......... .......... .......... 9.48M 1227500K .......... .......... .......... .......... .......... 299K 1227550K .......... .......... .......... .......... .......... 10.4M 1227600K .......... .......... .......... .......... .......... 9.38M 1227650K .......... .......... .......... .......... .......... 12.0M 1227700K .......... .......... .......... .......... .......... 11.5M 1227750K .......... .......... .......... .......... .......... 9.85M 1227800K .......... .......... .......... .......... .......... 247K 1227850K .......... .......... .......... .......... .......... 13.2M 1227900K .......... .......... .......... .......... .......... 532K 1227950K .......... .......... .......... .......... .......... 15.7M 1228000K .......... .......... .......... .......... .......... 19.8M 1228050K .......... .......... .......... .......... .......... 23.8M 1228100K .......... .......... .......... .......... .......... 33.4M 1228150K .......... .......... .......... .......... .......... 571K 1228200K .......... .......... .......... .......... .......... 11.0M 1228250K .......... .......... .......... .......... .......... 10.5M 1228300K .......... .......... .......... .......... .......... 12.0M 1228350K .......... .......... .......... .......... .......... 11.1M 1228400K .......... .......... .......... .......... .......... 10.6M 1228450K .......... .......... .......... .......... .......... 1.27M 1228500K .......... .......... .......... .......... .......... 374K 1228550K .......... .......... .......... .......... .......... 11.0M 1228600K .......... .......... .......... .......... .......... 11.2M 1228650K .......... .......... .......... .......... .......... 12.3M 1228700K .......... .......... .......... .......... .......... 11.6M 1228750K .......... .......... .......... .......... .......... 11.6M 1228800K .......... .......... .......... .......... .......... 295K 1228850K .......... .......... .......... .......... .......... 11.6M 1228900K .......... .......... .......... .......... .......... 11.9M 1228950K .......... .......... .......... .......... .......... 9.56M 1229000K .......... .......... .......... .......... .......... 10.9M 1229050K .......... .......... .......... .......... .......... 14.5M 1229100K .......... .......... .......... .......... .......... 12.2M 1229150K .......... .......... .......... .......... .......... 296K 1229200K .......... .......... .......... .......... .......... 11.8M 1229250K .......... .......... .......... .......... .......... 11.2M 1229300K .......... .......... .......... .......... .......... 12.0M 1229350K .......... .......... .......... .......... .......... 10.5M 1229400K .......... .......... .......... .......... .......... 12.1M 1229450K .......... .......... .......... .......... .......... 11.9M 1229500K .......... .......... .......... .......... .......... 245K 1229550K .......... .......... .......... .......... .......... 10.8M 1229600K .......... .......... .......... .......... .......... 11.7M 1229650K .......... .......... .......... .......... .......... 11.6M 1229700K .......... .......... .......... .......... .......... 12.0M 1229750K .......... .......... .......... .......... .......... 11.0M 1229800K .......... .......... .......... .......... .......... 297K 1229850K .......... .......... .......... .......... .......... 13.2M 1229900K .......... .......... .......... .......... .......... 11.9M 1229950K .......... .......... .......... .......... .......... 8.50M 1230000K .......... .......... .......... .......... .......... 13.2M 1230050K .......... .......... .......... .......... .......... 11.5M 1230100K .......... .......... .......... .......... .......... 12.3M 1230150K .......... .......... .......... .......... .......... 296K 1230200K .......... .......... .......... .......... .......... 14.5M 1230250K .......... .......... .......... .......... .......... 11.9M 1230300K .......... .......... .......... .......... .......... 10.7M 1230350K .......... .......... .......... .......... .......... 10.3M 1230400K .......... .......... .......... .......... .......... 11.6M 1230450K .......... .......... .......... .......... .......... 11.4M 1230500K .......... .......... .......... .......... .......... 299K 1230550K .......... .......... .......... .......... .......... 11.4M 1230600K .......... .......... .......... .......... .......... 11.9M 1230650K .......... .......... .......... .......... .......... 8.88M 1230700K .......... .......... .......... .......... .......... 17.3M 1230750K .......... .......... .......... .......... .......... 9.29M 1230800K .......... .......... .......... .......... .......... 11.7M 1230850K .......... .......... .......... .......... .......... 298K 1230900K .......... .......... .......... .......... .......... 13.7M 1230950K .......... .......... .......... .......... .......... 8.85M 1231000K .......... .......... .......... .......... .......... 12.1M 1231050K .......... .......... .......... .......... .......... 15.8M 1231100K .......... .......... .......... .......... .......... 10.8M 1231150K .......... .......... .......... .......... .......... 9.94M 1231200K .......... .......... .......... .......... .......... 247K 1231250K .......... .......... .......... .......... .......... 11.8M 1231300K .......... .......... .......... .......... .......... 9.64M 1231350K .......... .......... .......... .......... .......... 13.5M 1231400K .......... .......... .......... .......... .......... 12.5M 1231450K .......... .......... .......... .......... .......... 11.5M 1231500K .......... .......... .......... .......... .......... 11.7M 1231550K .......... .......... .......... .......... .......... 139K 1231600K .......... .......... .......... .......... .......... 17.9M 1231650K .......... .......... .......... .......... .......... 28.4M 1231700K .......... .......... .......... .......... .......... 21.4M 1231750K .......... .......... .......... .......... .......... 21.0M 1231800K .......... .......... .......... .......... .......... 14.8M 1231850K .......... .......... .......... .......... .......... 22.9M 1231900K .......... .......... .......... .......... .......... 27.3M 1231950K .......... .......... .......... .......... .......... 26.0M 1232000K .......... .......... .......... .......... .......... 20.2M 1232050K .......... .......... .......... .......... .......... 21.5M 1232100K .......... .......... .......... .......... .......... 24.6M 1232150K .......... .......... .......... .......... .......... 297K 1232200K .......... .......... .......... .......... .......... 8.90M 1232250K .......... .......... .......... .......... .......... 7.73M 1232300K .......... .......... .......... .......... .......... 9.98M 1232350K .......... .......... .......... .......... .......... 6.42M 1232400K .......... .......... .......... .......... .......... 298K 1232450K .......... .......... .......... .......... .......... 9.66M 1232500K .......... .......... .......... .......... .......... 8.40M 1232550K .......... .......... .......... .......... .......... 7.90M 1232600K .......... .......... .......... .......... .......... 9.12M 1232650K .......... .......... .......... .......... .......... 7.96M 1232700K .......... .......... .......... .......... .......... 248K 1232750K .......... .......... .......... .......... .......... 9.37M 1232800K .......... .......... .......... .......... .......... 12.2M 1232850K .......... .......... .......... .......... .......... 11.7M 1232900K .......... .......... .......... .......... .......... 11.8M 1232950K .......... .......... .......... .......... .......... 668K 1233000K .......... .......... .......... .......... .......... 486K 1233050K .......... .......... .. 15.4M=13m22s 2020-11-10 11:06:21 (1.50 MB/s) - 'crime.csv' saved [1262665799] 파일을 다운로드 받은 후에는 가상 데이터프레임으로 읽어들인다. 구체적인 데이터를 아직 모르기 때문에 우선 문자열 자료형으로 읽어들인다. 또 옵션을 로 해서 오류가 나는 데이터는 생략하도록 한다. Dask DataFrame Structure | | ID | Case Number | Date | Block | IUCR | Primary Type | Description | Location Description | Arrest | Domestic | Beat | District | Ward | Community Area | FBI Code | X Coordinate | Y Coordinate | Year | Updated On | Latitude | Longitude | Location | |---------------|--------|-------------|--------|--------|--------|--------------|-------------|-----------------------|--------|----------|-------|----------|-------|----------------|----------|--------------|--------------|-------|------------|----------|-----------|----------| | npartitions=20| | | | | | | | | | | | | | | | | | | | | | | | | object | object | object | object | object | object | object | object | object | object | object| object | object| object | object | object | object | object| object | object | object | object | | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | Dask Name: read-csv, 20 tasks 데이터 샘플 | Index | ID | Case Number | Date | Block | IUCR | Primary Type | Description | Location Description | Arrest | Domestic | ... | Ward | Community Area | FBI Code | X Coordinate | Y Coordinate | Year | Updated On | Latitude | Longitude | Location | |---------|--------------------------|-------------|----------------------|------------------|-------|--------------|-----------------|-----------------------|--------|----------|-----|------|----------------|----------|--------------|--------------|------|------------|----------|-----------|----------| | 196819 | 7026076 | HR432572 | 07/15/2009 04:00:00 PM | 007XX E 79TH ST | 0620 | BURGLARY | UNLAWFUL ENTRY | APARTMENT | false | false | ... | 6 | 44 | 05 | 1182734 | 1852793 | 2009 | 02/28/ | NaN | NaN | NaN | | 196820 | "error" : true | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | | 196821 | "message" : "Internal error" | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | | 196822 | "status" : 500 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | | 196823 | | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 표 설명: 열 이름: ID, Case Number, Date, Block, IUCR, Primary Type, 등. 총 22개의 열 중 일부만 표시되었습니다. 데이터는 오류와 누락된 값이 포함되어 있습니다. 이제 이 데이터프레임으로 분석을 시작해보자. 데이터 크기가 큰 만큼 시간이 오래 걸리기 때문에 친절하게도 Dask는 작업 진행도를 알 수 있는 라는 것을 제공한다. 다음과 같이 를 만들고 등록한다. 일단 등록하면 작업의 진행도를 프로그레스바 형태로 알려준다. 우선 각 열의 데이터 개수를 세어보자. [########################################] | 100% Completed | 21.9s Wall time: 21.9 s ID 5366005 Case Number 5365999 Date 5366001 Block 5366001 IUCR 5366001 Primary Type 5366001 Description 5366001 Location Description 5359222 Arrest 5366001 Domestic 5366001 Beat 5366001 District 5365954 Ward 4751265 Community Area 4753051 FBI Code 5366001 X Coordinate 5285054 Y Coordinate 5285054 Year 5366001 Updated On 5366001 Latitude 5285053 Longitude 5285053 Location 5285053 dtype: int64 데이터양이 많은 경우 이렇게 데이터 수를 세는 것만으로도 20초가 넘게 걸렸다. Dask는 이러한 대량 데이터의 분석 작업을 돕기 위한 작업 스케줄러(task scheduler)라는 것을 제공한다. 작업 스케줄러는 하나의 작업을 여러개의 쓰레드, 프로세스, 노드 등이 나누어 분담하도록 한다. 현재 Dask에서 제공하는 스케줄러의 종류는 다음과 같다. dask.get : 단일 쓰레드 dask.threaded.get : 멀티 쓰레드 풀(pool) dask.mutiprocessing.get : 멀티 프로세스 풀 distributed.Client.get : 여러대의 컴퓨터에서 분산 처리 병렬처리를 위해서는 어떠한 병렬 처리 방식을 사용할지, 작업 프로세스의 갯수는 어떻게 할지 등은 명령에서 인수로 설정해야 한다. 다음 코드는 멀티프로세싱을 하고 2개의 CPU 코어를 동시에 사용하도록 설정한 예이다. (물론 이 코드가 실행되는 컴튜터가 실제로 2개 이상의 코어를 가지고 있어야 성능이 개선된다.) [########################################] | 100% Completed | 15.6s Wall time: 15.6 s ID 5366005 Case Number 5365999 Date 5366001 Block 5366001 IUCR 5366001 Primary Type 5366001 Description 5366001 Location Description 5359222 Arrest 5366001 Domestic 5366001 Beat 5366001 District 5365954 Ward 4751265 Community Area 4753051 FBI Code 5366001 X Coordinate 5285054 Y Coordinate 5285054 Year 5366001 Updated On 5366001 Latitude 5285053 Longitude 5285053 Location 5285053 dtype: int64 실행되는 컴퓨터의 사양에 따라 다르긴 하겠지만, 처리속도가 빨라진 것을 볼 수 있다. 좀더 자세히 알아 보고 싶다면, 공식 문서를 살펴보자. > DASK Document (https://docs.dask.org/) 출처 : 데이터사이언스 스쿨(http://datascienceschool.net)
2020년 11월 01일27분DataFrame 시계열 자료 다루기
PythonDatetimeIndex 인덱스 시계열 자료는 인덱스가 날짜 혹은 시간인 데이터를 말한다. Pandas에서 시계열 자료를 생성하려면 인덱스를 자료형으로 만들어야 한다. 는 특정한 순간에 기록된 타임스탬프(timestamp) 형식의 시계열 자료를 다루기 위한 인덱스이다. 타임스탬프 인덱스의 라벨값이 반드시 일정한 간격일 필요는 없다. 인덱스는 다음과 같은 보조 함수를 사용하여 생성한다. pd.to_datetime 함수 pd.date_range 함수 함수를 쓰면 날짜/시간을 나타내는 문자열을 자동으로 datetime 자료형으로 바꾼 후 자료형 인덱스를 생성한다. DatetimeIndex(['2018-01-01', '2018-01-04', '2018-01-05', '2018-01-06'], dtype='datetime64[ns]', freq=None) 이렇게 만들어진 인덱스를 사용하여 시리즈나 데이터프레임을 생성하면 된다. 2018-01-01 1.764052 2018-01-04 0.400157 2018-01-05 0.978738 2018-01-06 2.240893 dtype: float64 함수를 쓰면 모든 날짜/시간을 일일히 입력할 필요없이 시작일과 종료일 또는 시작일과 기간을 입력하면 범위 내의 인덱스를 생성해 준다. DatetimeIndex(['2018-04-01', '2018-04-02', '2018-04-03', '2018-04-04', '2018-04-05', '2018-04-06', '2018-04-07', '2018-04-08', '2018-04-09', '2018-04-10', '2018-04-11', '2018-04-12', '2018-04-13', '2018-04-14', '2018-04-15', '2018-04-16', '2018-04-17', '2018-04-18', '2018-04-19', '2018-04-20', '2018-04-21', '2018-04-22', '2018-04-23', '2018-04-24', '2018-04-25', '2018-04-26', '2018-04-27', '2018-04-28', '2018-04-29', '2018-04-30'], dtype='datetime64[ns]', freq='D') DatetimeIndex(['2018-04-01', '2018-04-02', '2018-04-03', '2018-04-04', '2018-04-05', '2018-04-06', '2018-04-07', '2018-04-08', '2018-04-09', '2018-04-10', '2018-04-11', '2018-04-12', '2018-04-13', '2018-04-14', '2018-04-15', '2018-04-16', '2018-04-17', '2018-04-18', '2018-04-19', '2018-04-20', '2018-04-21', '2018-04-22', '2018-04-23', '2018-04-24', '2018-04-25', '2018-04-26', '2018-04-27', '2018-04-28', '2018-04-29', '2018-04-30'], dtype='datetime64[ns]', freq='D') 인수로 특정한 날짜만 생성되도록 할 수도 있다. 많이 사용되는 인수값은 다음과 같다. s: 초 T: 분 H: 시간 D: 일(day) B: 주말이 아닌 평일 W: 주(일요일) W-MON: 주(월요일) M: 각 달(month)의 마지막 날 MS: 각 달의 첫날 BM: 주말이 아닌 평일 중에서 각 달의 마지막 날 BMS: 주말이 아닌 평일 중에서 각 달의 첫날 WOM-2THU: 각 달의 두번째 목요일 Q-JAN: 각 분기의 첫달의 마지막 날 Q-DEC: 각 분기의 마지막 달의 마지막 날 보다 자세한 내용은 다음 웹사이트를 참조하자. https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#dateoffset-objects DatetimeIndex(['2018-04-02', '2018-04-03', '2018-04-04', '2018-04-05', '2018-04-06', '2018-04-09', '2018-04-10', '2018-04-11', '2018-04-12', '2018-04-13', '2018-04-16', '2018-04-17', '2018-04-18', '2018-04-19', '2018-04-20', '2018-04-23', '2018-04-24', '2018-04-25', '2018-04-26', '2018-04-27', '2018-04-30'], dtype='datetime64[ns]', freq='B') DatetimeIndex(['2018-01-07', '2018-01-14', '2018-01-21', '2018-01-28', '2018-02-04', '2018-02-11', '2018-02-18', '2018-02-25', '2018-03-04', '2018-03-11', '2018-03-18', '2018-03-25', '2018-04-01', '2018-04-08', '2018-04-15', '2018-04-22', '2018-04-29', '2018-05-06', '2018-05-13', '2018-05-20', '2018-05-27', '2018-06-03', '2018-06-10', '2018-06-17', '2018-06-24', '2018-07-01', '2018-07-08', '2018-07-15', '2018-07-22', '2018-07-29', '2018-08-05', '2018-08-12', '2018-08-19', '2018-08-26', '2018-09-02', '2018-09-09', '2018-09-16', '2018-09-23', '2018-09-30', '2018-10-07', '2018-10-14', '2018-10-21', '2018-10-28', '2018-11-04', '2018-11-11', '2018-11-18', '2018-11-25', '2018-12-02', '2018-12-09', '2018-12-16', '2018-12-23', '2018-12-30'], dtype='datetime64[ns]', freq='W-SUN') DatetimeIndex(['2018-01-01', '2018-01-08', '2018-01-15', '2018-01-22', '2018-01-29', '2018-02-05', '2018-02-12', '2018-02-19', '2018-02-26', '2018-03-05', '2018-03-12', '2018-03-19', '2018-03-26', '2018-04-02', '2018-04-09', '2018-04-16', '2018-04-23', '2018-04-30', '2018-05-07', '2018-05-14', '2018-05-21', '2018-05-28', '2018-06-04', '2018-06-11', '2018-06-18', '2018-06-25', '2018-07-02', '2018-07-09', '2018-07-16', '2018-07-23', '2018-07-30', '2018-08-06', '2018-08-13', '2018-08-20', '2018-08-27', '2018-09-03', '2018-09-10', '2018-09-17', '2018-09-24', '2018-10-01', '2018-10-08', '2018-10-15', '2018-10-22', '2018-10-29', '2018-11-05', '2018-11-12', '2018-11-19', '2018-11-26', '2018-12-03', '2018-12-10', '2018-12-17', '2018-12-24', '2018-12-31'], dtype='datetime64[ns]', freq='W-MON') shift 연산 시계열 데이터의 인덱스는 시간이나 날짜를 나타내기 때문에 날짜 이동 등의 다양한 연산이 가능하다. 예를 들어 연산을 사용하면 인덱스는 그대로 두고 데이터만 이동할 수도 있다. 2018-01-31 1.764052 2018-02-28 0.400157 2018-03-31 0.978738 2018-04-30 2.240893 Freq: M, dtype: float64 2018-01-31 NaN 2018-02-28 1.764052 2018-03-31 0.400157 2018-04-30 0.978738 Freq: M, dtype: float64 2018-01-31 0.400157 2018-02-28 0.978738 2018-03-31 2.240893 2018-04-30 NaN Freq: M, dtype: float64 2018-02-28 1.764052 2018-03-31 0.400157 2018-04-30 0.978738 2018-05-31 2.240893 Freq: M, dtype: float64 2018-02-04 1.764052 2018-03-04 0.400157 2018-04-01 0.978738 2018-05-06 2.240893 dtype: float64 resample 연산 resample 연산을 쓰면 시간 간격을 재조정하는 리샘플링(resampling)이 가능하다. 이 때 시간 구간이 작아지면 데이터 양이 증가한다고 해서 업-샘플링(up-sampling)이라 하고 시간 구간이 커지면 데이터 양이 감소한다고 해서 다운-샘플링(down-sampling)이라 부른다. 2018-03-22 0.625231 2018-03-23 -1.602058 2018-03-24 -1.104383 2018-03-25 0.052165 2018-03-26 -0.739563 2018-03-27 1.543015 2018-03-28 -1.292857 2018-03-29 0.267051 2018-03-30 -0.039283 2018-03-31 -1.168093 2018-04-01 0.523277 2018-04-02 -0.171546 2018-04-03 0.771791 2018-04-04 0.823504 2018-04-05 2.163236 2018-04-06 1.336528 2018-04-07 -0.369182 2018-04-08 -0.239379 2018-04-09 1.099660 2018-04-10 0.655264 Freq: D, dtype: float64 다운-샘플링의 경우에는 원래의 데이터가 그룹으로 묶이기 때문에 그룹바이(groupby)때와 같이 그룹 연산을 해서 대표값을 구해야 한다. 2018-01-07 0.697206 2018-01-14 0.468797 2018-01-21 0.249052 2018-01-28 0.301938 2018-02-04 0.023198 2018-02-11 0.283486 2018-02-18 -0.096136 2018-02-25 -0.446708 2018-03-04 0.155312 2018-03-11 0.200799 2018-03-18 -0.376808 2018-03-25 -0.895860 2018-04-01 -0.129493 2018-04-08 0.616422 2018-04-15 0.877462 Freq: W-SUN, dtype: float64 2018-01-31 -1.173123 2018-02-28 0.676433 2018-03-31 0.087551 2018-04-30 0.523277 Freq: M, dtype: float64 날짜가 아닌 시/분 단위에서는 구간위 왼쪽 한계값(가장 빠른 값)은 포함하고 오른쪽 한계값(가장 늦은 값)은 포함하지 않는다. 즉, 가장 늦은 값은 다음 구간에 포함된다. 예를 들어 10분 간경으로 구간을 만들면 10의 배수가 되는 시각은 구간의 시작점이 된다. 2018-01-01 00:00:00 2.259309 2018-01-01 00:01:00 -0.042257 2018-01-01 00:02:00 -0.955945 2018-01-01 00:03:00 -0.345982 2018-01-01 00:04:00 -0.463596 2018-01-01 00:05:00 0.481481 2018-01-01 00:06:00 -1.540797 2018-01-01 00:07:00 0.063262 2018-01-01 00:08:00 0.156507 2018-01-01 00:09:00 0.232181 2018-01-01 00:10:00 -0.597316 2018-01-01 00:11:00 -0.237922 2018-01-01 00:12:00 -1.424061 2018-01-01 00:13:00 -0.493320 2018-01-01 00:14:00 -0.542861 2018-01-01 00:15:00 0.416050 2018-01-01 00:16:00 -1.156182 2018-01-01 00:17:00 0.781198 2018-01-01 00:18:00 1.494485 2018-01-01 00:19:00 -2.069985 Freq: T, dtype: float64 2018-01-01 00:00:00 -0.155837 2018-01-01 00:10:00 -3.829915 2018-01-01 00:20:00 -0.115280 2018-01-01 00:30:00 -3.234560 2018-01-01 00:40:00 -4.452304 2018-01-01 00:50:00 -0.906751 Freq: 10T, dtype: float64 왼쪽이 아니라 오른쪽 한계값을 구간에 포함하려면 인수를 사용한다. 이 때는 10의 배수가 되는 시각이 앞 구간에 포함된다. 2017-12-31 23:50:00 2.259309 2018-01-01 00:00:00 -3.012462 2018-01-01 00:10:00 -2.806340 2018-01-01 00:20:00 -1.354903 2018-01-01 00:30:00 -4.004135 2018-01-01 00:40:00 -3.180252 2018-01-01 00:50:00 -0.595865 Freq: 10T, dtype: float64 매서드는 구간의 시고저종(open, high, low, close)값을 구한다. | 시간 | open | high | low | close | |--------------------------|-----------|-----------|-----------|-----------| | 2018-01-01 00:00:00 | 2.259309 | 2.259309 | -0.955945 | -0.463596 | | 2018-01-01 00:05:00 | 0.481481 | 0.481481 | -1.540797 | 0.232181 | | 2018-01-01 00:10:00 | -0.597316 | -0.237922 | -1.424061 | -0.542861 | | 2018-01-01 00:15:00 | 0.416050 | 1.494485 | -2.069985 | -2.069985 | | 2018-01-01 00:20:00 | 0.426259 | 0.676908 | -0.637437 | -0.132881 | | 2018-01-01 00:25:00 | -0.297791 | 1.152332 | -1.676004 | 1.079619 | | 2018-01-01 00:30:00 | -0.813364 | 0.521065 | -1.466424 | 0.141953 | | 2018-01-01 00:35:00 | -0.319328 | 0.694749 | -1.383364 | -1.383364 | | 2018-01-01 00:40:00 | -1.582938 | 0.610379 | -1.582938 | -0.596314 | | 2018-01-01 00:45:00 | -0.052567 | 0.523891 | -1.936280 | 0.088422 | | 2018-01-01 00:50:00 | -0.310886 | 1.955912 | -2.772593 | 1.955912 | | 2018-01-01 00:55:00 | 0.390093 | 0.493742 | -0.652409 | -0.116104 | 업-샘플링의 경우에는 실제로 존재하지 않는 데이터를 만들어야 한다. 이 때는 앞에서 나온 데이터를 뒤에서 그대로 쓰는 forward filling 방식과 뒤에서 나올 데이터를 앞에서 미리 쓰는 backward filling 방식을 사용할 수 있다. 각각 , 매서드를 이용한다. 2018-01-01 00:00:00 2.259309 2018-01-01 00:00:30 2.259309 2018-01-01 00:01:00 -0.042257 2018-01-01 00:01:30 -0.042257 2018-01-01 00:02:00 -0.955945 2018-01-01 00:02:30 -0.955945 2018-01-01 00:03:00 -0.345982 2018-01-01 00:03:30 -0.345982 2018-01-01 00:04:00 -0.463596 2018-01-01 00:04:30 -0.463596 2018-01-01 00:05:00 0.481481 2018-01-01 00:05:30 0.481481 2018-01-01 00:06:00 -1.540797 2018-01-01 00:06:30 -1.540797 2018-01-01 00:07:00 0.063262 2018-01-01 00:07:30 0.063262 2018-01-01 00:08:00 0.156507 2018-01-01 00:08:30 0.156507 2018-01-01 00:09:00 0.232181 2018-01-01 00:09:30 0.232181 Freq: 30S, dtype: float64 dt 접근자 datetime 자료형 시리즈에는 dt접근자가 있어 datetime 자료형이 가진 몇가지 유용한 속성과 매서드를 사용할 수 있다. 0 2020-12-25 1 2020-12-26 2 2020-12-27 3 2020-12-28 4 2020-12-29 ... 95 2021-03-30 96 2021-03-31 97 2021-04-01 98 2021-04-02 99 2021-04-03 Length: 100, dtype: datetime64[ns] 예를 들어 , , , 등의 속성을 이용하면 년, 월, 일, 요일 정보를 빼낼 수 있다. 0 2020 1 2020 2 2020 3 2020 4 2020 ... 95 2021 96 2021 97 2021 98 2021 99 2021 Length: 100, dtype: int64 0 4 1 5 2 6 3 0 4 1 .. 95 1 96 2 97 3 98 4 99 5 Length: 100, dtype: int64 매서드를 이용하여 문자열을 만드는 것도 가능하다. 0 2020년 12월 25일 1 2020년 12월 26일 2 2020년 12월 27일 3 2020년 12월 28일 4 2020년 12월 29일 ... 95 2021년 03월 30일 96 2021년 03월 31일 97 2021년 04월 01일 98 2021년 04월 02일 99 2021년 04월 03일 Length: 100, dtype: object Quiz, 다음 명령으로 만들어진 데이터프레임에 대해 월별 value의 합계를 구하라. (힌트 : 매서드와 접근자를 사용)) 출처 : 데이터사이언스 스쿨(http://datascienceschool.net)
2020년 10월 31일11분DataFrame 피봇과 그룹
Python피봇테이블 피봇테이블(pivot table)이란 데이터 열 중에서 두개의 열을 각각 행 인덱스, 열 인덱스로 사용하여 데이터를 조회하여 펼쳐놓은 것을 말한다. Pandas는 피봇테이블을 만들기 위한 매서드를 제공한다. 첫번째 인수로는 행 인덱스로 사용할 열 이름, 두뻔째 인수로는 열 인덱스로 사용할 열 이름, 그리고 마지막으로 데이터로 사용할 열 이름을 넣는다. Pandas는 지정된 두 열을 각각 행 인덱스와 열 인덱스로 바꾼 후 행 인덱스 라벨 값이 첫번째 키의 값과 같고 열 인덱스의 라벨 값이 두번째 키의 값과 같은 데이터를 찾아서 해당 칸에 넣는다. 만약 주어진 데이터가 존재하지 않으면 해당 칸에 값을 넣는다. 다음 데이터는 각 도시의 연도별 인구를 나타낸 것이다. | | 도시 | 연도 | 인구 | 지역 | |---|--------|-------|-----------|--------| | 0 | 서울 | 2015 | 9904312 | 수도권 | | 1 | 서울 | 2010 | 9631482 | 수도권 | | 2 | 서울 | 2005 | 9762546 | 수도권 | | 3 | 부산 | 2015 | 3448737 | 경상권 | | 4 | 부산 | 2010 | 3393191 | 경상권 | | 5 | 부산 | 2005 | 3512547 | 경상권 | | 6 | 인천 | 2015 | 2890451 | 수도권 | | 7 | 인천 | 2010 | 263203 | 수도권 | 이 데이터를 도시 이름이 열 인덱스가 되고 연도가 행 인덱스가 되어 행과 열 인덱스만 보면 어떤 도시의 어떤 시점의 인구를 쉽게 알수 있도록 피봇테이블로 만들어 보자. 명령으로 사용하고 행 인덱스 인수로는 , 열 인덱스 인수로는 , 데이터 이름으로 를 입력하면 된다. | 도시 | 2005 | 2010 | 2015 | |--------|------------|------------|------------| | 부산 | 3512547.0 | 3393191.0 | 3448737.0 | | 서울 | 9762546.0 | 9631482.0 | 9904312.0 | | 인천 | NaN | 263203.0 | 2890451.0 | 이 피봇테이블의 값 3512547은 "도시"가 부산이고 "연도"가 2005년인 데이터를 "인구"열에서 찾은 값이다. 2005년 인천의 인구는 데이터에 없기 때문에 으로 표시된다. 피봇테이블은 다음과 같이 명령과 명령을 사용하서 만들 수도 있다. | 도시 | 2005 | 2010 | 2015 | |--------|------------|------------|------------| | 부산 | 3512547.0 | 3393191.0 | 3448737.0 | | 서울 | 9762546.0 | 9631482.0 | 9904312.0 | | 인천 | NaN | 263203.0 | 2890451.0 | 행 인덱스나 열 인덱스를 리스트로 주는 경우에는 다중 인덱스 피봇 테이블을 생성한다. | | 연도 | 2005 | 2010 | 2015 | | --- | --- | --- | --- | --- | | 지역 | 도시 | | | | | 경상권 | 부산 | 3512547.0 | 3393191.0 | 3448737.0 | | 수도권 | 서울 | 9762546.0 | 9631482.0 | 9904312.0 | | 인천 | NaN | 263203.0 | 2890451.0 | 행 인덱스와 열 인덱스는 데이터를 찾는 키(key)의 역할을 한다. 따라서 키 값으로 데이터가 단 하나만 찾아져야 한다. 만약 행 인덱스와 열 인덱스 조건을 만족하는 데이터가 2개 이상인 경우에는 에러가 발생한다. 예를 들어 위 데이터프레임에서 ("지역", "연도")를 키로 하면 ("수도권", "2015")에 해당하는 값이 두개 이상이므로 다음과 같이 에러가 발생한다. ValueError: Index contains duplicate entries, cannot reshape 그룹분석 만약 키가 지정하는 조건에 맞는 데이터가 하나 이상이라서 데이터 그룹을 이루는 경우에는 그룹의 특성을 보여주는 그룹분석(group analysis)을 해야 한다. 그룹분석은 피봇테이블과 달리 키에 의해서 결정되는 데이터가 여러개가 있을 경우 미리 지정한 연선을 통해 그 그룹 데이터의 대표값을 계산한다. Pandas에서는 매서드를 사용하여 다음처럼 그룹분석을 한다. > 1. 분석하고자 하는 시리즈나 데이터프레임에 매서드를 호출하여 그룹화를 한다. > 2. 그룹 객체에 대해 그룹연산을 수행한다. 그룹연산 매서드 결과, 즉 클래스 객체의 뒤에 붙일 수 있는 그룹연산 매서드는 다양하다. 다음은 자주 사용되는 그룹연산 매서드들이다. , : 그룹 데이터의 개수 , , , : 그룹 데이터의 평균, 중앙값, 최소, 최대 , , , , : 그룹 데이터의 합계, 곱, 표준편차, 분산, 사분위수 , : 그룹 데이터중 가장 첫번째 데이터와 가장 나중 데이터와 이 외에도 많이 사용되는 것으로는 다음과 같은 그룹 연산이 있다. , 만약 원하는 구릅연산이 없는 경우 함수를 만들고 이 함수를 에 전달한다. 또는 여러가지 그룹연산을 동시에 하고 싶은 경우 함수 이름 문자열의 리스트를 전달한다. 하나의 그룹 대표값이 아니라 여러개의 값을 데이터프레임으로 구한다. 처럼 하나의 대표값이 아닌 데이터프레임을 출력하지만 원하는 그룹연산이 없는 경우에 사용한다. 그룹에 대한 대표값을 만드는 것이 아니라 그룹별 계산을 통해 데이터 자체를 변형한다. 예를 들어 다음과 같은 데이터가 있을 때 key1의 값(A 또는 B)에 따른 data1의 평균은 어떻게 구할까? | | key1 | key2 | data1 | data2 | | --- | --- | --- | --- | --- | | 0 | A | one | 1 | 10 | | 1 | A | two | 2 | 20 | | 2 | B | one | 3 | 30 | | 3 | B | two | 4 | 40 | | 4 | A | one | 5 | 50 | 명령을 사용하여 그룹 A와 그룹 B로 구분한 그룹 데이터를 만든다. 이 클래스 객체에는 각 그룹 데이터의 인덱스를 저장한 groups 속성이 있다. A그룹과 B그룹 데이터의 합계를 구하기 위해 이라는 그룹연산을 한다. | | data1 | data2 | | --- | --- | --- | | key1 | | | | A | 8 | 80 | | B | 7 | 70 | 클래스 객체를 명시적으로 얻을 필요가 없다면 매서드와 그룹연산 메서드를 연속으로 호출한다. 다음 예제는 열 에 대해서만 그룹연산을 하는 코드 이다. key1 A 8 B 7 Name: data1, dtype: int64 데이터를 그룹으로 나눈 클래스 객체 또는 그룹분석한 결과에서 만 뽑아도 된다. key1 A 8 B 7 Name: data1, dtype: int64 key1 A 8 B 7 Name: data1, dtype: int64 이번에는 복합 키 (key1, key2) 값에 따른 data1의 합계를 구하자. 분석하고자 하는 키가 복수이면 리스트를 사용한다. key1 key2 A one 6 two 2 B one 3 two 4 Name: data1, dtype: int64 이 결과를 명령으로 피봇 테이블 행태로 만들수도 있다. | key2 | one | two | | --- | --- | --- | | key1 | | | | A | 6 | 2 | | B | 3 | 4 | 그룹분석 기능을 사용하면 위의 인구 데이터로부터 지역별 합계를 구할 수도 있다. | | 도시 | 연도 | 인구 | 지역 | | --- | --- | --- | --- | --- | | 0 | 서울 | 2015 | 9904312 | 수도권 | | 1 | 서울 | 2010 | 9631482 | 수도권 | | 2 | 서울 | 2005 | 9762546 | 수도권 | | 3 | 부산 | 2015 | 3448737 | 경상권 | | 4 | 부산 | 2010 | 3393191 | 경상권 | | 5 | 부산 | 2005 | 3512547 | 경상권 | | 6 | 인천 | 2015 | 2890451 | 수도권 | | 7 | 인천 | 2010 | 263203 | 수도권 | | 연도 | 2005 | 2010 | 2015 | | --- | --- | --- | --- | | 지역 | | | | | 경상권 | 3512547 | 3393191 | 3448737 | | 수도권 | 9762546 | 9894685 | 12794763 | 다음 데이터는 150 송이의 붓꽃(iris)에 대해 붓꽃 종(species)별로 꽃받침길이(sepallangth), 꽃받침폭(sepalwidth), 꽃잎길이(tetallength), 꽃잎폭(petalwidth)을 측정한 데이터이다. | | sepallength | sepalwidth | petallength | petalwidth | species | | --- | --- | --- | --- | --- | --- | | 0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa | | 1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa | | 2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa | | 3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa | | 4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa | | ... | ... | ... | ... | ... | ... | | 145 | 6.7 | 3.0 | 5.2 | 2.3 | virginica | | 146 | 6.3 | 2.5 | 5.0 | 1.9 | virginica | | 147 | 6.5 | 3.0 | 5.2 | 2.0 | virginica | | 148 | 6.2 | 3.4 | 5.4 | 2.3 | virginica | | 149 | 5.9 | 3.0 | 5.1 | 1.8 | virginica | 각 붓꽃 종별로 가장 큰 값과 가장 작은 값의 비율을 구해보자. 이러한 계산을 하는 그룹연산 매서드는 없으므로 직접 함수로 만들고 매서드를 이용해야 한다. | | sepallength | sepalwidth | petallength | petalwidth | | --- | --- | --- | --- | --- | | species | | | | | | setosa | 1.348837 | 1.913043 | 1.900000 | 6.000000 | | versicolor | 1.428571 | 1.700000 | 1.700000 | 1.800000 | | virginica | 1.612245 | 1.727273 | 1.533333 | 1.785714 | 매서드를 사용하면 다양한 기술 통계(descriptive statistics)값을 한 번에 구한다. 그룹별로 하나의 스칼라 값이 아니라 하나의 데이터프레임이 생성된다는 점에 주의하자. | | species | setosa | versicolor | virginica | | --- | --- | --- | --- | --- | | sepal_length | count | 50.000000 | 50.000000 | 50.000000 | | mean | 5.006000 | 5.936000 | 6.588000 | | std | 0.352490 | 0.516171 | 0.635880 | | min | 4.300000 | 4.900000 | 4.900000 | | 25% | 4.800000 | 5.600000 | 6.225000 | | 50% | 5.000000 | 5.900000 | 6.500000 | | 75% | 5.200000 | 6.300000 | 6.900000 | | max | 5.800000 | 7.000000 | 7.900000 | | sepal_width | count | 50.000000 | 50.000000 | 50.000000 | | mean | 3.428000 | 2.770000 | 2.974000 | | std | 0.379064 | 0.313798 | 0.322497 | | min | 2.300000 | 2.000000 | 2.200000 | | 25% | 3.200000 | 2.525000 | 2.800000 | | 50% | 3.400000 | 2.800000 | 3.000000 | | 75% | 3.675000 | 3.000000 | 3.175000 | | max | 4.400000 | 3.400000 | 3.800000 | | petal_length | count | 50.000000 | 50.000000 | 50.000000 | | mean | 1.462000 | 4.260000 | 5.552000 | | std | 0.173664 | 0.469911 | 0.551895 | | min | 1.000000 | 3.000000 | 4.500000 | | 25% | 1.400000 | 4.000000 | 5.100000 | | 50% | 1.500000 | 4.350000 | 5.550000 | | 75% | 1.575000 | 4.600000 | 5.875000 | | max | 1.900000 | 5.100000 | 6.900000 | | petal_width | count | 50.000000 | 50.000000 | 50.000000 | | mean | 0.246000 | 1.326000 | 2.026000 | | std | 0.105386 | 0.197753 | 0.274650 | | min | 0.100000 | 1.000000 | 1.400000 | | 25% | 0.200000 | 1.200000 | 1.800000 | | 50% | 0.200000 | 1.300000 | 2.000000 | | 75% | 0.300000 | 1.500000 | 2.300000 | | max | 0.600000 | 1.800000 | 2.500000 | 매서드를 사용하면 매서드처럼 하나의 그룹에 대해 하나의 대표값(스칼라 값)을 구하는게 아니라 데이터프레임을 만들 수 있다. 예를 들어 다음처럼 각 붓꽃 종별로 가장 꽃잎길이(petal length)가 큰 3개의 데이터를 뽑아낼 수도 있다. | | | sepallength | sepalwidth | petallength | petalwidth | species | | --- | --- | --- | --- | --- | --- | --- | | species | | | | | | | | setosa | 24 | 4.8 | 3.4 | 1.9 | 0.2 | setosa | | 44 | 5.1 | 3.8 | 1.9 | 0.4 | setosa | | 23 | 5.1 | 3.3 | 1.7 | 0.5 | setosa | | versicolor | 83 | 6.0 | 2.7 | 5.1 | 1.6 | versicolor | | 77 | 6.7 | 3.0 | 5.0 | 1.7 | versicolor | | 72 | 6.3 | 2.5 | 4.9 | 1.5 | versicolor | | virginica | 118 | 7.7 | 2.6 | 6.9 | 2.3 | virginica | | 117 | 7.7 | 3.8 | 6.7 | 2.2 | virginica | | 122 | 7.7 | 2.8 | 6.7 | 2.0 | virginica | 매서드는 그룹별 대표값을 만드는 것이 아니라 그룹별 계산을 통해 데이터프레임 자체를 변화시킨다. 따라서 만들어진 데이터프레임의 크기는 원래 데이터프레임과 같다. 예를 들어 다음처럼 각 붓꽃 꽃잎길이가 해당 종 내에서 대/중/소 어느 것에 해당되는지에 대한 데이터프레임을 만들 수도 있다. | | petallength | petallength_class | | --- | --- | --- | | 140 | 5.6 | 중 | | 141 | 5.1 | 소 | | 142 | 5.1 | 소 | | 143 | 5.9 | 대 | | 144 | 5.7 | 중 | | 145 | 5.2 | 소 | | 146 | 5.0 | 소 | | 147 | 5.2 | 소 | | 148 | 5.4 | 중 | | 149 | 5.1 | 소 | Pandas는 명령과 명령의 중간 성격을 가지는 명령도 제공한다. 명령은 명령처럼 그룹분석을 하지만 최종적으로는 명령처럼 피봇테이블을 만든다. 즉 명령의 결과에 을 자동 적용하여 2차원적인 형태로 변형한다. 사용 방법은 다음과 같다. pivottable(data, values=None, index=None, columns=None, aggfunc='mean', fillvalue=None, margins=False, margins_name='All') data: 분석할 데이터프레임 (메서드일 때는 필요하지 않음) values: 분석할 데이터프레임에서 분석할 열 index: 행 인덱스로 들어갈 키 열 또는 키 열의 리스트 columns: 열 인덱스로 들어갈 키 열 또는 키 열의 리스트 aggfunc: 분석 메서드 fill_value: NaN 대체 값 margins: 모든 데이터를 분석한 결과를 오른쪽과 아래에 붙일지 여부 margins_name: 마진 열(행)의 이름 만약 조건에 따른 데이터가 유일하게 선택되지 않으면 그룹연산을 하며 이 때 인수로 정의된 함수를 수행하여 대표값을 계산한다. 를 메서드로 사용할 때는 객체 자체가 데이터가 되므로 인수가 필요하지 않다. 예를 들어 위에서 만들었던 피봇테이블은 명령으로 다음과 같이 만들 수도 있다. 인수의 순서에 주의해야 한다. | 연도 | 2005 | 2010 | 2015 | | --- | --- | --- | --- | | 도시 | | | | | 부산 | 3512547.0 | 3393191.0 | 3448737.0 | | 서울 | 9762546.0 | 9631482.0 | 9904312.0 | | 인천 | NaN | 263203.0 | 2890451.0 | 인수를 주면 로 주어진 분석 방법을 해당 열의 모든 데이터, 해당 행의 모든 데이터 그리고 전체 데이터에 대해 적용한 결과를 같이 보여준다. 가 주어지지 않았으면 평균을 계산한다. | 연도 | 2005 | 2010 | 2015 | 평균 | | --- | --- | --- | --- | --- | | 도시 | | | | | | 부산 | 3512547.0 | 3393191.0 | 3448737.0 | 3.451492e+06 | | 서울 | 9762546.0 | 9631482.0 | 9904312.0 | 9.766113e+06 | | 인천 | NaN | 263203.0 | 2890451.0 | 1.576827e+06 | | 평균 | 6637546.5 | 4429292.0 | 5414500.0 | 5.350809e+06 | 이 결과에서 가장 오른쪽 합계 열의 첫번째 값 3451492은 모든 부산 인구 데이터의 평균, 두번째 값 9766113은 모든 서울 인구 데이터의 평균이다. 가장 아래의 합계 행의 첫번째 값은 2005년 데이터의 평균값, 두번째 값은 2010년 데이터의 평균값이다. 가장 오른쪽 아래의 값 5350809는 전체 데이터의 평균값이다. 다음 계산을 통해 이를 확인할 수 있다. 5350808.625 행 인덱스나 열 인덱스에 리스트를 넣으면 다중 인덱스 테이블을 만든다. | | | 인구 | | --- | --- | --- | | 연도 | 도시 | | | 2005 | 부산 | 3512547 | | 서울 | 9762546 | | 2010 | 부산 | 3393191 | | 서울 | 9631482 | | 인천 | 263203 | | 2015 | 부산 | 3448737 | | 서울 | 9904312 | | 인천 | 2890451 | 식당에서 식사 후 내는 팁(tip)과 관련된 데이터를 이용하여 좀더 구체적으로 그룹분석 방법을 살펴본다. 우선 Seaborn 패키지에 설치된 샘플 데이터를 로드한다. 이 데이터프레임에서 각각의 컬럼은 다음을 뜻한다. total_bill: 식사대금 tip: 팁 sex: 성별 smoker: 흡연/금연 여부 day: 요일 time: 시간 size: 인원 | | total_bill | tip | sex | smoker | day | time | size | | --- | --- | --- | --- | --- | --- | --- | --- | | 239 | 29.03 | 5.92 | Male | No | Sat | Dinner | 3 | | 240 | 27.18 | 2.00 | Female | Yes | Sat | Dinner | 2 | | 241 | 22.67 | 2.00 | Male | Yes | Sat | Dinner | 2 | | 242 | 17.82 | 1.75 | Male | No | Sat | Dinner | 2 | | 243 | 18.78 | 3.00 | Female | No | Thur | Dinner | 2 | 분석의 목표는 식사 대금 대비 팁의 비율이 어떤 경우에 가장 높아지지는 찾는 것이다. 우선 식사대금와 팁의 비율을 나타내는 를 추가하자. | | totalbill | tip | sex | smoker | day | time | size | tippct | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 239 | 29.03 | 5.92 | Male | No | Sat | Dinner | 3 | 0.203927 | | 240 | 27.18 | 2.00 | Female | Yes | Sat | Dinner | 2 | 0.073584 | | 241 | 22.67 | 2.00 | Male | Yes | Sat | Dinner | 2 | 0.088222 | | 242 | 17.82 | 1.75 | Male | No | Sat | Dinner | 2 | 0.098204 | | 243 | 18.78 | 3.00 | Female | No | Thur | Dinner | 2 | 0.159744 | 다음으로 각 열의 데이터에 대해 간단히 분포를 알아본다. | | totalbill | tip | size | tippct | | --- | --- | --- | --- | --- | | count | 244.000000 | 244.000000 | 244.000000 | 244.000000 | | mean | 19.785943 | 2.998279 | 2.569672 | 0.160803 | | std | 8.902412 | 1.383638 | 0.951100 | 0.061072 | | min | 3.070000 | 1.000000 | 1.000000 | 0.035638 | | 25% | 13.347500 | 2.000000 | 2.000000 | 0.129127 | | 50% | 17.795000 | 2.900000 | 2.000000 | 0.154770 | | 75% | 24.127500 | 3.562500 | 3.000000 | 0.191475 | | max | 50.810000 | 10.000000 | 6.000000 | 0.710345 | 우선 성별로 나누어 데이터 갯수를 세어보자 | | totalbill | tip | smoker | day | time | size | tippct | | --- | --- | --- | --- | --- | --- | --- | --- | | sex | | | | | | | | | Male | 157 | 157 | 157 | 157 | 157 | 157 | 157 | | Female | 87 | 87 | 87 | 87 | 87 | 87 | 87 | 데이터 갯수의 경우 NaN 데이터가 없다면 모두 같은 값이 나올 것이다. 이 때는 명령을 사용하면 더 간단히 표시된다. 명령은 NaN이 있어도 상관하지 않는다. sex Male 157 Female 87 dtype: int64 이번에는 성별과 흡연유무로 나누어 데이터 갯수를 알아보자. sex smoker Male Yes 60 No 97 Female Yes 33 No 54 dtype: int64 좀더 보기 좋게 피봇 테이블 형태로 바꿀 수도 있다. | smoker | Yes | No | All | | --- | --- | --- | --- | | sex | | | | | Male | 60 | 97 | 157 | | Female | 33 | 54 | 87 | | All | 93 | 151 | 244 | 이제 성별과 흡연 여부에 따른 평균 팁 비율을 살펴보자 | | tip_pct | | --- | --- | | sex | | | Male | 0.157651 | | Female | 0.166491 | | | tip_pct | | --- | --- | | smoker | | | Yes | 0.163196 | | No | 0.159328 | 명령을 사용할 수도 있다. | | tip_pct | | --- | --- | | sex | | | Male | 0.157651 | | Female | 0.166491 | | | | tip_pct | | --- | --- | --- | | sex | smoker | | | Male | Yes | 0.152771 | | No | 0.160669 | | Female | Yes | 0.182150 | | No | 0.156921 | | smoker | Yes | No | | --- | --- | --- | | sex | | | | Male | 0.152771 | 0.160669 | | Female | 0.182150 | 0.156921 | 여성 혹은 흡연자의 팁 비율이 높은 것을 볼 수 있다. 하지만 이 데이터에는 평균을 제외한 분산(variance) 등의 다른 통계값이 없으므로 명령으로 여러가지 통계값을 한 번에 알아본다. | | tip_pct | | --- | --- | | | count | mean | std | min | 25% | 50% | 75% | max | | sex | | | | | | | | | | Male | 157.0 | 0.157651 | 0.064778 | 0.035638 | 0.121389 | 0.153492 | 0.186240 | 0.710345 | | Female | 87.0 | 0.166491 | 0.053632 | 0.056433 | 0.140416 | 0.155581 | 0.194266 | 0.416667 | | | tip_pct | | --- | --- | | | count | mean | std | min | 25% | 50% | 75% | max | | smoker | | | | | | | | | | Yes | 93.0 | 0.163196 | 0.085119 | 0.035638 | 0.106771 | 0.153846 | 0.195059 | 0.710345 | | No | 151.0 | 0.159328 | 0.039910 | 0.056797 | 0.136906 | 0.155625 | 0.185014 | 0.291990 | | | | tip_pct | | --- | --- | --- | | | | count | mean | std | min | 25% | 50% | 75% | max | | sex | smoker | | | | | | | | | | Male | Yes | 60.0 | 0.152771 | 0.090588 | 0.035638 | 0.101845 | 0.141015 | 0.191697 | 0.710345 | | No | 97.0 | 0.160669 | 0.041849 | 0.071804 | 0.131810 | 0.157604 | 0.186220 | 0.291990 | | Female | Yes | 33.0 | 0.182150 | 0.071595 | 0.056433 | 0.152439 | 0.173913 | 0.198216 | 0.416667 | | No | 54.0 | 0.156921 | 0.036421 | 0.056797 | 0.139708 | 0.149691 | 0.181630 | 0.252672 | 이번에는 각 그룹에서 가장 많은 팁과 가장 적은 팁의 차이를 알아보자. 이 계산을 해 줄 수 있는 그룹연산 함수가 없으므로 함수를 직접 만들고 메서드를 사용한다. | | | tip | | --- | --- | --- | | sex | smoker | | | Male | Yes | 9.00 | | No | 7.75 | | Female | Yes | 5.50 | | No | 4.20 | 만약 여러가지 그룹연산을 동시에 하고 싶다면 다음과 같이 리스트를 이용한다. | | | total_bill | | --- | --- | --- | | | | mean | peaktopeak | | sex | smoker | | | | Male | Yes | 22.284500 | 43.56 | | No | 19.791237 | 40.82 | | Female | Yes | 17.977879 | 41.23 | | No | 18.105185 | 28.58 | 만약 데이터 열마다 다른 연산을 하고 싶다면 열 라벨과 연산 이름(또는 함수)를 딕셔너리로 넣는다. | | | tippct | totalbill | | --- | --- | --- | --- | | sex | smoker | | | | Male | Yes | 0.152771 | 43.56 | | No | 0.160669 | 40.82 | | Female | Yes | 0.182150 | 41.23 | | No | 0.156921 | 28.58 | 다음은 명령으로 더 복잡한 분석을 한 예이다. | | | size | tip_pct | | --- | --- | --- | --- | | | smoker | Yes | No | Yes | No | | sex | day | | | | | | Male | Thur | 2.300000 | 2.500000 | 0.164417 | 0.165706 | | Fri | 2.125000 | 2.000000 | 0.144730 | 0.138005 | | Sat | 2.629630 | 2.656250 | 0.139067 | 0.162132 | | Sun | 2.600000 | 2.883721 | 0.173964 | 0.158291 | | Female | Thur | 2.428571 | 2.480000 | 0.163073 | 0.155971 | | Fri | 2.000000 | 2.500000 | 0.209129 | 0.165296 | | Sat | 2.200000 | 2.307692 | 0.163817 | 0.147993 | | Sun | 2.500000 | 3.071429 | 0.237075 | 0.165710 | | | | day | Thur | Fri | Sat | Sun | | --- | --- | --- | --- | --- | --- | --- | | time | sex | smoker | | | | | | Lunch | Male | Yes | 23 | 5 | 0 | 0 | | No | 50 | 0 | 0 | 0 | | Female | Yes | 17 | 6 | 0 | 0 | | No | 60 | 3 | 0 | 0 | | Dinner | Male | Yes | 0 | 12 | 71 | 39 | | No | 0 | 4 | 85 | 124 | | Female | Yes | 0 | 8 | 33 | 10 | | No | 2 | 2 | 30 | 43 | 피봇테이블과 그룹연산에 대해 알아 보았다. 분석 연습을 해보기 위해 자주 사용되는 타이타닉 승객데이터를 이용해서 연습 해보면 좋을 것 같다. 출처 : 데이터사이언스 스쿨(http://datascienceschool.net)
2020년 10월 18일32분DataFrame 합성
PythonPandas는 두 개 이상의 DataFrame을 하나로 합치는 데이터 병합(merge)이나 연결(concatenate)을 지원한다. 함수를 사용한 DataFrame 병합 함수는 두 데이터프레임의 공통 열 혹은 인덱스를 기준으로 두 개의 테이블을 합친다. 이 때 기준이 되는 열, 행의 데이터를 키(Key)라고 한다. | 인덱스 | 고객번호 | 이름 | | --- | --- | --- | | 0 | 1001 | 둘리 | | 1 | 1002 | 도우너 | | 2 | 1003 | 또치 | | 3 | 1004 | 길동 | | 4 | 1005 | 마이콜 | | 5 | 1006 | 희동 | | 6 | 1007 | 영희 | | 인덱스 | 고객번호 | 금액 | | --- | --- | --- | | 0 | 1001 | 10000 | | 1 | 1001 | 20000 | | 2 | 1005 | 15000 | | 3 | 1006 | 5000 | | 4 | 1008 | 100000 | | 5 | 1001 | 30000 | 함수로 위의 두 데이터프레임 df1, df2 를 합치면 공통열인 열을 기준으로 데이터를 찾아서 합친다. 이 때 기본적으로는 양쪽 데이터프레임에 모두 키가 존재하는 데이터만 보여주는 inner join 방식을 사용한다. | 인덱스 | 고객번호 | 이름 | 금액 | | --- | --- | --- | --- | | 0 | 1001 | 둘리 | 10000 | | 1 | 1001 | 둘리 | 20000 | | 2 | 1001 | 둘리 | 30000 | | 3 | 1005 | 마이콜 | 15000 | | 4 | 1006 | 희동 | 5000 | outer join 방식은 키 값이 한쪽에만 있어도 데이터를 보여준다. | 인덱스 | 고객번호 | 이름 | 금액 | | --- | --- | --- | --- | | 0 | 1001 | 둘리 | 10000.0 | | 1 | 1001 | 둘리 | 20000.0 | | 2 | 1001 | 둘리 | 30000.0 | | 3 | 1002 | 도우너 | NaN | | 4 | 1003 | 또치 | NaN | | 5 | 1004 | 길동 | NaN | | 6 | 1005 | 마이콜 | 15000.0 | | 7 | 1006 | 희동 | 5000.0 | | 8 | 1007 | 영희 | NaN | | 9 | 1008 | NaN | 100000.0 | left, right 방식은 각각 첫번째, 혹은 두번째 데이터프레임의 키 앖을 모두 보여준다. | 인덱스 | 고객번호 | 이름 | 금액 | | --- | --- | --- | --- | | 0 | 1001 | 둘리 | 10000.0 | | 1 | 1001 | 둘리 | 20000.0 | | 2 | 1001 | 둘리 | 30000.0 | | 3 | 1002 | 도우너 | NaN | | 4 | 1003 | 또치 | NaN | | 5 | 1004 | 길동 | NaN | | 6 | 1005 | 마이콜 | 15000.0 | | 7 | 1006 | 희동 | 5000.0 | | 8 | 1007 | 영희 | NaN | | | 고객번호 | 이름 | 금액 | |---|--------|------|--------| | 0 | 1001 | 둘리 | 10000 | | 1 | 1001 | 둘리 | 20000 | | 2 | 1001 | 둘리 | 30000 | | 3 | 1005 | 마이콜 | 15000 | | 4 | 1006 | 희동 | 5000 | | 5 | 1008 | NaN | 100000 | 만약 테이블에 키 값이 같은 데이터가 여러개 있는 경우에는 있을 수 있는 모든 경우의 수를 따져서 조합을 만들어 낸다. | | 품종 | 꽃잎길이 | |---|---------|--------| | 0 | setosa | 1.4 | | 1 | setosa | 1.3 | | 2 | virginica | 1.5 | | 3 | virginica | 1.3 | | | 품종 | 꽃잎너비 | |---|-----------|--------| | 0 | setosa | 0.4 | | 1 | virginica | 0.3 | | 2 | virginica | 0.5 | | 3 | versicolor| 0.3 | 이 데이터에서 키 값 setosa에 대해 왼쪽 데이터프레임에는 1.4와 1.3 이라는 2개의 데이터, 오른쪽 데이터프레임에는 0.4라는 1개의 데이터가 있으므로 병합된 데이터에는 setosa가 (1.4, 0.4), (1.3, 0.4) 두 개의 데이터가 생긴다. 키값 virginica의 경우에는 왼쪽 데이터프레임에 1.5와 1.3이라는 2개의 데이터, 오른쪽 데이터프레임에 0.3와 0.5라는 2개의 데이터가 있으므로 2개와 2개의 조합에 의해 4가지 값이 생긴다. | | 품종 | 꽃잎길이 | 꽃잎너비 | |---|-----------|--------|--------| | 0 | setosa | 1.4 | 0.4 | | 1 | setosa | 1.3 | 0.4 | | 2 | virginica | 1.5 | 0.3 | | 3 | virginica | 1.5 | 0.5 | | 4 | virginica | 1.3 | 0.3 | | 5 | virginica | 1.3 | 0.5 | 두 데이터프레임에서 이름이 같은 열은 모두 키가 된다. 만약 이름이 같아도 키가 되면 안되는 열이 있다면 인수로 기준열을 명시해야 한다. 다음 예에서 첫번째 데이터프레임의 데이터는 실제로는 금액을 나타내는 데이터 이고 두번째 데이터프레임의 데이터는 실제로 성별을 나타내는 데이터이므로 이름이 같아도 다른 데이터이다. 따라서 이 열은 기준열이 되면 안된다. | | 고객명 | 날짜 | 데이터 | |---|-------|------------|--------| | 0 | 춘향 | 2018-01-01 | 20000 | | 1 | 춘향 | 2018-01-02 | 30000 | | 2 | 몽룡 | 2018-01-01 | 100000 | | | 고객명 | 데이터 | |---|-------|--------| | 0 | 춘향 | 여자 | | 1 | 몽룡 | 남자 | | | 고객명 | 날짜 | 데이터x | 데이터y | |---|-------|------------|----------|----------| | 0 | 춘향 | 2018-01-01 | 20000 | 여자 | | 1 | 춘향 | 2018-01-02 | 30000 | 여자 | | 2 | 몽룡 | 2018-01-01 | 100000 | 남자 | 이 때 기준 열이 아니면서 이름이 같은 열에는 또는 와 같은 접미사가 붙는다. 반대로 키가 되는 기준열의 이름이 두 데이터프레임에서 다르면 , 인수를 사용하여 기준열을 명시해야 한다. | | 이름 | 성정 | |---|------|------| | 0 | 영희 | 1 | | 1 | 철수 | 2 | | 2 | 철수 | 3 | | | 성명 | 성적2 | |---|------|-------| | 0 | 영희 | 4 | | 1 | 영희 | 5 | | 2 | 철수 | 6 | | | 이름 | 성정 | 성명 | 성적2 | |---|------|------|------|-------| | 0 | 영희 | 1 | 영희 | 4 | | 1 | 영희 | 1 | 영희 | 5 | | 2 | 철수 | 2 | 철수 | 6 | | 3 | 철수 | 3 | 철수 | 6 | 일반 데이터 열이 아닌 인덱스를 기준열로 사용하여면 또는 인수를 로 설정한다. | | 도시 | 연도 | 인구 | |---|------|------|----------| | 0 | 서울 | 2000 | 9853972 | | 1 | 서울 | 2005 | 9762546 | | 2 | 서울 | 2010 | 9631482 | | 3 | 부산 | 2000 | 3655437 | | 4 | 부산 | 2005 | 3512547 | | | | 데이터1 | 데이터2 | |---|-----|---------|---------| | 부산 | 2000 | 0 | 1 | | 부산 | 2005 | 2 | 3 | | 서울 | 2000 | 4 | 5 | | 서울 | 2005 | 6 | 7 | | 서울 | 2010 | 8 | 9 | | 서울 | 2015 | 10 | 11 | | | 도시 | 연도 | 인구 | 데이터1 | 데이터2 | |---|------|------|----------|---------|---------| | 0 | 서울 | 2000 | 9853972 | 4 | 5 | | 1 | 서울 | 2005 | 9762546 | 6 | 7 | | 2 | 서울 | 2010 | 9631482 | 8 | 9 | | 3 | 부산 | 2000 | 3655437 | 0 | 1 | | 4 | 부산 | 2005 | 3512547 | 2 | 3 | | | 서울 | 부산 | |---|------|------| | a | 1.0 | 2.0 | | c | 3.0 | 4.0 | | e | 5.0 | 6.0 | | | 대구 | 광주 | |---|-------|-------| | b | 7.0 | 8.0 | | c | 9.0 | 10.0 | | d | 11.0 | 12.0 | | e | 13.0 | 14.0 | | | 서울 | 부산 | 대구 | 광주 | |---|-------|-------|-------|-------| | a | 1.0 | 2.0 | NaN | NaN | | b | NaN | NaN | 7.0 | 8.0 | | c | 3.0 | 4.0 | 9.0 | 10.0 | | d | NaN | NaN | 11.0 | 12.0 | | e | 5.0 | 6.0 | 13.0 | 14.0 | join 매서드 명령어 대신 매서드를 사용할 수도 있다. | | 서울 | 부산 | 대구 | 광주 | |---|-------|-------|-------|-------| | a | 1.0 | 2.0 | NaN | NaN | | b | NaN | NaN | 7.0 | 8.0 | | c | 3.0 | 4.0 | 9.0 | 10.0 | | d | NaN | NaN | 11.0 | 12.0 | | e | 5.0 | 6.0 | 13.0 | 14.0 | 함수를 사용한 데이터 연결 함수를 사용하면 기준 열(key column)을 사용하지 않고 단순히 데이터를 연결(concatenate)한다. 기본적으로는 위/아래로 데이터 행을 연결한다. 단순히 두 시리즈나 데이터프레임을 연결하기 때문에 인덱스 값이 중복될 수 있다. A 0 B 1 dtype: int64 A 2 B 3 C 4 dtype: int64 A 0 B 1 A 2 B 3 C 4 dtype: int64 내부적으로 기본값은 이기 때문에 만약 옆으로 데이터 열을 연결하고 싶으면 로 인수를 설정한다. | | 데이터1 | 데이터2 | |---|---------|---------| | a | 0 | 1 | | b | 2 | 3 | | c | 4 | 5 | | | 데이터3 | 데이터4 | |---|---------|---------| | a | 5 | 6 | | c | 7 | 8 | | | 데이터1 | 데이터2 | 데이터3 | 데이터4 | |---|---------|---------|---------|---------| | a | 0 | 1 | 5.0 | 6.0 | | b | 2 | 3 | NaN | NaN | | c | 4 | 5 | 7.0 | 8.0 | 출처 : 데이터사이언스 스쿨(http://datascienceschool.net)
2020년 10월 09일14분DataFrame 인덱스 조작
PythonDataFrame 인덱스 설정 및 제거 DataFrame에 인덱스로 들어가 있어야 할 데이터가 일반 데이터 열에 들어가 있거나 반대로 일반 데이터 열이어야 할 것이 인덱스로 되어 있을 수 있다. 이 때는 명령이나 명령으로 인덱스와 일반 데이터 열을 교환할 수 있다. set_index : 기존의 행 인덱스를 제거하고 데이터 열 중 하나를 인덱스로 설정 reset_index : 기존의 행 인덱스를 제거하고 인덱스를 데이터 열로 추가 array([['A', 'B', 'C', 'D', 'E'], ['0.67', '0.21', '0.13', '0.32', '0.36'], ['0.57', '0.44', '0.99', '0.1', '0.21'], ['0.16', '0.65', '0.25', '0.47', '0.24']], dtype='U32') | index | C1 | C2 | C3 | C4 | |-------|----|----|----|----| | 0 | A | 0.55 | 0.65 | 0.79 | | 1 | B | 0.72 | 0.44 | 0.53 | | 2 | C | 0.6 | 0.89 | 0.57 | | 3 | D | 0.54 | 0.96 | 0.93 | | 4 | E | 0.42 | 0.38 | 0.07 | 명령으로 C1열을 인덱스로 설정할 수 있다. 이때 기존 인덱스는 없어진다. | C1 | C2 | C3 | C4 | |----|----|----|----| | A | 0.55 | 0.65 | 0.79 | | B | 0.72 | 0.44 | 0.53 | | C | 0.6 | 0.89 | 0.57 | | D | 0.54 | 0.96 | 0.93 | | E | 0.42 | 0.38 | 0.07 | 마찬가지로 C2열을 인덱스로 지정하면 기존의 인덱스는 사라진다. | C2 | C3 | C4 | |----|----|----| | 0.55 | 0.65 | 0.79 | | 0.72 | 0.44 | 0.53 | | 0.6 | 0.89 | 0.57 | | 0.54 | 0.96 | 0.93 | | 0.42 | 0.38 | 0.07 | 명령으로 인덱스를 보통의 자료형으로 바꿀 수도 있다. 이 때 인덱스 열은 자료형의 가장 선두로 삽입된다. DataFrame의 인덱스는 정수로 된 디폴트 인덱스로 바뀌게 된다. | index | C1 | C2 | C3 | C4 | |-------|----|----|----|----| | 0 | A | 0.55 | 0.65 | 0.79 | | 1 | B | 0.72 | 0.44 | 0.53 | | 2 | C | 0.6 | 0.89 | 0.57 | | 3 | D | 0.54 | 0.96 | 0.93 | | 4 | E | 0.42 | 0.38 | 0.07 | 명령 사용시에 로 설정하면 인덱스 열을 보통의 자료열로 올리는 것이 아니라 그냥 버리게 된다. | index | C2 | C3 | C4 | |-------|----|----|----| | 0 | 0.55 | 0.65 | 0.79 | | 1 | 0.72 | 0.44 | 0.53 | | 2 | 0.6 | 0.89 | 0.57 | | 3 | 0.54 | 0.96 | 0.93 | | 4 | 0.42 | 0.38 | 0.07 | 다중 인덱스 행이나 열에 여러 계층을 가지는 인덱스 즉, 다중 인덱스(multi-index)를 설정할 수도 있다. DataFrame을 생성할 때 인수에 다음 예제처럼 리스트의 리스트(행렬) 형태로 인덱스를 넣으면 다중 열 인덱스를 가지게 된다. | index | AC1 | AC2 | BC1 | BC2 | |-------|------|------|------|------| | 0 | 1.76 | 0.40 | 0.98 | 2.24 | | 1 | 1.87 | -0.98 | 0.95 | -0.15 | | 2 | -0.10 | 0.41 | 0.14 | 1.45 | | 3 | 0.76 | 0.12 | 0.44 | 0.33 | | 4 | 1.49 | -0.21 | 0.31 | -0.85 | 다중 인덱스는 이름을 지정하면 더 편리하게 사용할 수 있다. 열 인덱스들의 이름 지정은 객체의 속성에 리스트를 넣어서 지정한다. | Cidx1 | AC1 | AC2 | BC1 | BC2 | |-------|------|------|------|------| | 0 | 1.76 | 0.40 | 0.98 | 2.24 | | 1 | 1.87 | -0.98 | 0.95 | -0.15 | | 2 | -0.10 | 0.41 | 0.14 | 1.45 | | 3 | 0.76 | 0.12 | 0.44 | 0.33 | | 4 | 1.49 | -0.21 | 0.31 | -0.85 | 마찬가지로 DataFrame을 생성할 때 인수에 리스트의 리스트(행렬) 형태로 인덱스를 넣으면 다중 (행) 인덱스를 가진다. 행 인덱스들의 이름 지정은 객체의 속성에 리스트를 넣어서 지정한다. | Ridx1 | Ridx2 | AC | AD | BC | BD | |-------|-------|------|------|------|------| | M | id_1 | 1.76 | 0.40 | 0.98 | 2.24 | | M | id_2 | 1.87 | -0.98 | 0.95 | -0.15 | | M | id_3 | -0.10 | 0.41 | 0.14 | 1.45 | | F | id_1 | 0.76 | 0.12 | 0.44 | 0.33 | | F | id_2 | 1.49 | -0.21 | 0.31 | -0.85 | | F | id_3 | -2.55 | 0.65 | 0.86 | -0.74 | 행 인덱스와 열 인덱스 교환 명령이나 명령을 쓰면 열 인덱스를 행 인덱스로 바꾸거나 반대로 행 인덱스를 열 인덱스로 바꿀 수 있다. : 열 인덱스 -> 행 인덱스로 변환 : 행 인덱스 -> 열 인덱스로 변환 명령을 실행하면 열 인덱스가 반시계 방향으로 90도 회전한 것과 비슷한 모양이 된다. 마찬가지로 unstack 명령을 실행하면 행 인덱스가 시계 방향으로 90도 회전한 것과 비슷하다. 인덱스를 지정할 때는 문자열 이름과 순서를 표시하는 숫자 인덱스를 모두 사용할 수 있다. | Ridx1 | Ridx2 | Cidx1 | C | D | |-------|-------|--------|----|----| | M | id_1 | A | 1.76 | 0.40 | | M | id_1 | B | 0.98 | 2.24 | | M | id_2 | A | 1.87 | -0.98 | | M | id_2 | B | 0.95 | -0.15 | | M | id_3 | A | -0.10 | 0.41 | | M | id_3 | B | 0.14 | 1.45 | | F | id_1 | A | 0.76 | 0.12 | | F | id_1 | B | 0.44 | 0.33 | | F | id_2 | A | 1.49 | -0.21 | | F | id_2 | B | 0.31 | -0.85 | | F | id_3 | A | -2.55 | 0.65 | | F | id_3 | B | 0.86 | -0.74 | | Ridx1 | Ridx2 | Cidx2 | A | B | |-------|-------|--------|----|----| | M | id_1 | C | 1.76 | 0.98 | | M | id_1 | D | 0.40 | 2.24 | | M | id_2 | C | 1.87 | 0.95 | | M | id_2 | D | -0.98 | -0.15 | | M | id_3 | C | -0.10 | 0.14 | | M | id_3 | D | 0.41 | 1.45 | | F | id_1 | C | 0.76 | 0.44 | | F | id_1 | D | 0.12 | 0.33 | | F | id_2 | C | 1.49 | 0.31 | | F | id_2 | D | -0.21 | -0.85 | | F | id_3 | C | -2.55 | 0.86 | | F | id_3 | D | 0.65 | -0.74 | | Ridx1 | ACid1 | ACid2 | ACid3 | ADid1 | ADid2 | ADid3 | BCid1 | BCid2 | BCid3 | BDid1 | BDid2 | BDid3 | |-------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------| | F | 0.76 | 1.49 | -2.55 | 0.12 | -0.21 | 0.65 | 0.44 | 0.31 | 0.86 | 0.33 | -0.85 | -0.74 | | M | 1.76 | 1.87 | -0.10 | 0.40 | -0.98 | 0.41 | 0.98 | 0.95 | 0.14 | 2.24 | -0.15 | 1.45 | | Ridx2 | ACF | ACM | ADF | ADM | BCF | BCM | BDF | BDM | |-------|--------|--------|--------|--------|--------|--------|--------|--------| | id_1 | 0.76 | 1.76 | 0.12 | 0.40 | 0.44 | 0.98 | 0.33 | 2.24 | | id_2 | 1.49 | 1.87 | -0.21 | -0.98 | 0.31 | 0.95 | -0.85 | -0.15 | | id_3 | -2.55 | -0.10 | 0.65 | 0.41 | 0.86 | 0.14 | -0.74 | 1.45 | 다중 인덱스가 있는 경우 인덱싱 DataFrame이 다중 인덱스를 가지는 경우에는 인덱스가 하나의 라벨이나 숫자가 아니라 로 둘러싸인 튜플이 되어야 한다. 예를 들어 앞에서 만든 DataFrame의 경우 다음과 같이 인덱싱할 수 있다. | index | AC1 | AC2 | BC1 | BC2 | |-------|------|------|------|------| | 0 | 1.76 | 0.40 | 0.98 | 2.24 | | 1 | 1.87 | -0.98 | 0.95 | -0.15 | | 2 | -0.10 | 0.41 | 0.14 | 1.45 | | 3 | 0.76 | 0.12 | 0.44 | 0.33 | | 4 | 1.49 | -0.21 | 0.31 | -0.85 | 0 0.98 1 0.95 2 0.14 3 0.44 4 0.31 Name: (B, C1), dtype: float64 0.98 | index | AC1 | AC2 | BC1 | BC2 | |-------|------|------|------|------| | 0 | 1.76 | 0.40 | 100.00 | 2.24 | | 1 | 1.87 | -0.98 | 0.95 | -0.15 | | 2 | -0.10 | 0.41 | 0.14 | 1.45 | | 3 | 0.76 | 0.12 | 0.44 | 0.33 | | 4 | 1.49 | -0.21 | 0.31 | -0.85 | 만약 하나의 레벨 값만 넣으면 다중 인덱스 중에서 가장 상위의 값을 지정한 것으로 본다. | Cidx2 | C1 | C2 | |-------|----|----| | 0 | 1.76 | 0.40 | | 1 | 1.87 | -0.98 | | 2 | -0.10 | 0.41 | | 3 | 0.76 | 0.12 | | 4 | 1.49 | -0.21 | DataFrame은 다음과 같이 인덱싱할 수 있다. | Ridx1 | Ridx2 | AC | AD | BC | BD | |-------|-------|------|------|------|------| | M | id_1 | 1.76 | 0.40 | 0.98 | 2.24 | | M | id_2 | 1.87 | -0.98 | 0.95 | -0.15 | | M | id_3 | -0.10 | 0.41 | 0.14 | 1.45 | | F | id_1 | 0.76 | 0.12 | 0.44 | 0.33 | | F | id_2 | 1.49 | -0.21 | 0.31 | -0.85 | | F | id_3 | -2.55 | 0.65 | 0.86 | -0.74 | Ridx1 Ridx2 M id_1 1.76 id_2 1.87 id_3 -0.10 F id_1 0.76 id_2 1.49 id_3 -2.55 Name: (A, C), dtype: float64 Cidx1 Cidx2 A C 1.76 D 0.40 B C 0.98 D 2.24 Name: (M, id_1), dtype: float64 | | Cidx1 | A | B | |----|-------|--------------------|--------------------| | | Cidx2 | C | D | C | D | |----|-------|----------|----------|----------|----------| | Ridx1 | Ridx2 | | | | | |----|-------|----------|----------|----------|----------| | M | id_1 | 1.76 | 0.40 | 0.98 | 2.24 | | | id_2 | 1.87 | -0.98 | 0.95 | -0.15 | | | id_3 | -0.10 | 0.41 | 0.14 | 1.45 | | F | id_1 | 0.76 | 0.12 | 0.44 | 0.33 | | | id_2 | 1.49 | -0.21 | 0.31 | -0.85 | | | id_3 | -2.55 | 0.65 | 0.86 | -0.74 | | All | All | 6.46 | 0.78 | 7.36 | 4.56 | 다중 인덱스의 인덱스 순서 교환 다중 인덱스의 인덱스 순서를 바꾸고 싶으면 명령을 사용한다. ijaxissort_indexlevel` 인수를 사용하여 어떤 인덱스를 기준으로 정렬하는지 알려주어야 한다. | | Cidx2 | C | D | C | D | |----|---------|----------|----------|----------|----------| | | Cidx1 | A | A | B | B | |----|---------|----------|----------|----------|----------| | Ridx2 | Ridx1 | | | | | |----|---------|----------|----------|----------|----------| | All | All | 6.46 | 0.78 | 7.36 | 4.56 | | id_1 | F | 0.76 | 0.12 | 0.44 | 0.33 | | id_1 | M | 1.76 | 0.40 | 0.98 | 2.24 | | id_2 | F | 1.49 | -0.21 | 0.31 | -0.85 | | id_2 | M | 1.87 | -0.98 | 0.95 | -0.15 | | id_3 | F | -2.55 | 0.65 | 0.86 | -0.74 | | id_3 | M | -0.10 | 0.41 | 0.14 | 1.45 | | | Cidx2 | A | B | A | B | |----|---------|----------|----------|----------|----------| | | Cidx1 | C | D | C | D | |----|---------|----------|----------|----------|----------| | Ridx1 | Ridx2 | | | | | |----|---------|----------|----------|----------|----------| | All | All | 6.46 | 7.36 | 0.78 | 4.56 | | id_1 | F | 0.76 | 0.44 | 0.12 | 0.33 | | id_1 | M | 1.76 | 0.98 | 0.40 | 2.24 | | id_2 | F | 1.49 | 0.31 | -0.21 | -0.85 | | id_2 | M | 1.87 | 0.95 | -0.98 | -0.15 | | id_3 | F | -2.55 | 0.86 | 0.65 | -0.74 | | id_3 | M | -0.10 | 0.14 | 0.41 | 1.45 | 출처 : 데이터사이언스 스쿨(datascienceschool.net)
2020년 10월 06일16분Pandas DataFrame 데이터 조작
PythonPandas는 Numpy의 2차원 배열에서 가능한 대부분의 데이터 처리가 가능하며 추가로 데이터 처리 및 변환을 위한 다양한 함수와 메서드를 제공한다. 데이터 갯수 세기 가장 간단한 데이터 분석은 데이터의 갯수를 세는 것이나. 메서드를 사용하는데, 주의할 점은 NaN값은 세지 않는다는 것이다. 0 0.0 1 1.0 2 2.0 3 NaN 4 4.0 5 5.0 6 6.0 7 7.0 8 8.0 9 9.0 dtype: float64 9 데이터프레임에서는 각 열마다 별도로 데이터 갯수를 센다. 데이터에서 값이 누락된 부분을 찾을 때 유용하다. | index | 0 | 1 | 2 | 3 | |-------|----|----|----|----| | 0 | 0.0 | 0.0 | 3.0 | 2.0 | | 1 | 3.0 | 0.0 | 2.0 | 1.0 | | 2 | 3.0 | 2.0 | 4.0 | NaN | | 3 | 4.0 | 3.0 | 4.0 | 2.0 | 0 4 1 4 2 4 3 3 dtype: int64 카테고리 값 세기 Series의 값이 정수, 문자열, 카테고리 값인 경우에는 value_counts 매서드로 각각의 값이 나온 횟수를 셀 수 있다. 95 4 96 5 97 2 98 4 99 3 dtype: int32 1 22 0 18 4 17 5 16 3 14 2 13 dtype: int64 DataFrame에는 매서드가 없으므로 각 열마다 별도 확인 해야 한다. 3.0 2 4.0 1 0.0 1 Name: 0, dtype: int64 정렬 데이터를 정렬하려면 와 매서드를 사용한다. 는 인덱스 값을 기준으로, 는 데이터 값을 기준으로 정렬한다. 앞서 Series의 각 데이터 값에 따른 데이터 갯수를 보기좋게 정렬하려면 를 사용한다. 0 18 1 22 2 13 3 14 4 17 5 16 dtype: int64 NaN값이 있는 경우에는 정렬하면 NaN값은 마지막에 위치한다. 0 0.0 1 1.0 2 2.0 4 4.0 5 5.0 6 6.0 7 7.0 8 8.0 9 9.0 3 NaN dtype: float64 큰 수에서 작은 수로 반대방향 정렬하려면 인수를 지정한다. 9 9.0 8 8.0 7 7.0 6 6.0 5 5.0 4 4.0 2 2.0 1 1.0 0 0.0 3 NaN dtype: float64 DataFrame에서 매서드를 사용하려면 인수로 정렬 기준이 되는 열을 지정해 주어야 한다. | index | 0 | 1 | 2 | 3 | |-------|----|----|----|----| | 0 | 0.0 | 0.0 | 3.0 | 2.0 | | 1 | 3.0 | 0.0 | 2.0 | 1.0 | | 2 | 3.0 | 2.0 | 4.0 | NaN | | 3 | 4.0 | 3.0 | 4.0 | 2.0 | 인수에 리스트 값을 넣으면 이 순서대로 정렬 기준이 우선순위가 된다. | index | 0 | 1 | 2 | 3 | |-------|----|----|----|----| | 1 | 3.0 | 0.0 | 2.0 | 1.0 | | 0 | 0.0 | 0.0 | 3.0 | 2.0 | | 2 | 3.0 | 2.0 | 4.0 | NaN | | 3 | 4.0 | 3.0 | 4.0 | 2.0 | 행/열 합계 행과 열의 합계를 구할 때는 매서드를 사용한다. 행 합계 : axis=1 열 합계 : axis=0 (기본값이므로 생략 가능) | index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |-------|----|----|----|----|----|----|----|----| | 0 | 5 | 8 | 9 | 5 | 0 | 0 | 1 | 7 | | 1 | 6 | 9 | 2 | 4 | 5 | 2 | 4 | 2 | | 2 | 4 | 7 | 7 | 9 | 1 | 7 | 0 | 6 | | 3 | 9 | 9 | 7 | 6 | 9 | 1 | 0 | 1 | 0 35 1 34 2 41 3 42 dtype: int64 0 24 1 33 2 25 3 24 4 15 5 10 6 5 7 16 dtype: int64 행과 열의 합을 행/열에 추가해보자. | index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | row_sum | |-------|----|----|----|----|----|----|----|----|---------| | 0 | 5 | 8 | 9 | 5 | 0 | 0 | 1 | 7 | 35 | | 1 | 6 | 9 | 2 | 4 | 5 | 2 | 4 | 2 | 34 | | 2 | 4 | 7 | 7 | 9 | 1 | 7 | 0 | 6 | 41 | | 3 | 9 | 9 | 7 | 6 | 9 | 1 | 0 | 1 | 42 | | index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | row_sum | |-------|----|----|----|----|----|----|----|----|---------| | 0 | 5.0 | 8.0 | 9.0 | 5.0 | 0.0 | 0.0 | 1.0 | 7.0 | 35.0 | | 1 | 6.0 | 9.0 | 2.0 | 4.0 | 5.0 | 2.0 | 4.0 | 2.0 | 34.0 | | 2 | 4.0 | 7.0 | 7.0 | 9.0 | 1.0 | 7.0 | 0.0 | 6.0 | 41.0 | | 3 | 9.0 | 9.0 | 7.0 | 6.0 | 9.0 | 1.0 | 0.0 | 1.0 | 42.0 | | col_sum | 24.0 | 33.0 | 25.0 | 24.0 | 15.0 | 10.0 | 5.0 | 16.0 | 152.0 | apply 변환 행이나 열 단위로 더 복잡한 처리를 하고 싶을 때는 매서드를 사용한다. 인수로 행 또는 열을 받는 함수를 매서드의 인수로 넣으면 각 열(또는 행)을 반복하여 그 함수에 적용시킨다. | index | A | B | C | |-------|----|----|----| | 0 | 1 | 2 | 1 | | 1 | 3 | 3 | 5 | | 2 | 4 | 1 | 2 | | 3 | 3 | 2 | 4 | | 4 | 4 | 3 | 4 | 예를 들어 각 열의 최대값과 최소값의 차이를 구하고 싶으면 다음과 같은 람다 함수를 넣는다. A 3 B 2 C 4 dtype: int64 마찬가지로 행에 대해 적용하고 싶다면 인수를 추가해 주면 된다. 0 1 1 2 2 3 3 2 4 1 dtype: int64 각 열에 대해 어떤값이 얼마나 사용되고 있는지 알고 싶다면 함수를 넣을 수도 있다. | index | A | B | C | |-------|----|----|----| | 1 | 1.0 | 1.0 | 1.0 | | 2 | NaN | 2.0 | 1.0 | | 3 | 2.0 | 2.0 | NaN | | 4 | 2.0 | NaN | 2.0 | | 5 | NaN | NaN | 1.0 | NaN 값은 fillna 매서드를 사용하여 원하는 값으로 바꿀 수 있다. astype 매서드로 전체 데이터의 자료형을 바꾸는 것도 가능하다. | index | A | B | C | |-------|----|----|----| | 1 | 1 | 1 | 1 | | 2 | 0 | 2 | 1 | | 3 | 2 | 2 | 0 | | 4 | 2 | 0 | 2 | | 5 | 0 | 0 | 1 | 실수 값을 카테고리 값으로 변환 실수 값을 크기 기준으로 하여 카테고리 값으로 변환하고 싶을 때는 다음과 같은 명령을 사용한다. cut : 실수 값의 경계선을 지정하는 경우 qcut : 갯수가 똑같은 구간으로 나누는 경우 명령을 사용하면 실수값을 다음처럼 카테고리 값으로 바꿀 수 있다. 인수는 카테고리를 나누는 기준값이 된다. 영역을 넘는 값은 NaN으로 처리된다. [NaN, '미성년자', '미성년자', '청년', '청년', ..., '노년', '청년', '장년', '중년', NaN] Length: 12 Categories (5, object): ['미성년자' < '청년' < '중년' < '장년' < '노년'] 명령이 반환하는 값은 클래스 객체이다. 이 객체는 속성으로 라벨 문자열을, 속성으로 정수로 인코딩한 카테고리 값을 가진다. pandas.core.arrays.categorical.Categorical Index(['미성년자', '청년', '중년', '장년', '노년'], dtype='object') array([-1, 0, 0, 1, 1, 3, 2, 4, 1, 3, 2, -1], dtype=int8) | index | ages | age_cat | |-------|------|----------| | 0 | 0 | NaN | | 1 | 2 | 미성년자 | | 2 | 10 | 미성년자 | | 3 | 21 | 청년 | | 4 | 23 | 청년 | | 5 | 37 | 장년 | | 6 | 31 | 중년 | | 7 | 61 | 노년 | | 8 | 20 | 청년 | | 9 | 41 | 장년 | | 10 | 32 | 중년 | | 11 | 100 | NaN | 정말 쉽게 데이터를 정의한 카테고리기준으로 분류 하였다! 명령은 구각 경계선을 지정하지 않고 데이터 갯수가 같도록 지정한 수의 구간으로 나눈다. 예를 들어 다음 코드는 1000개의 데이터를 4개의 구간으로 나누는데 각 구간은 250개씩 데이터를 가진다. ['Q2', 'Q1', 'Q2', 'Q3', 'Q1', ..., 'Q1', 'Q1', 'Q4', 'Q4', 'Q2'] Length: 1000 Categories (4, object): ['Q1' < 'Q2' < 'Q3' < 'Q4'] Q4 250 Q3 250 Q2 250 Q1 250 dtype: int64 성적을 비유하여 생각해보면 은 절대평가 기준이 되는 점수에 의해 분류가 되는 것이고 은 상대평가 즉 점수의 순서대로 정렬하고 특정 수만큼씩 분류하는 것이라고 생각하면 될듯 싶다. 출처 : 데이터사이언스 스쿨(datascienceschool.net)
2020년 10월 03일12분Pandas DataFrame 인덱싱
PythonDataFrame 인덱싱 DataFrame에서 특정한 데이터만 골라내는 것을 인덱싱(indexing)이라고 한다. Pandas는 numpy행렬과 같이 쉼표를 사용한 형식의 2차원 인덱싱을 지원하기 위해 다음과 같은 특별한 인덱서(indexer) 속성을 제공한다. loc : 라벨값 기반의 2차원 인덱싱 iloc : 순서를 나타내는 정수 기반의 2차원 인덱싱 at : 라벨값 기반의 2차원 인덱싱 (한개의 스카라 값만 찾는다.) iat : 순서를 나타내는 정수 기반의 2차원 인덱싱 (한개의 스칼라 값만 찾는다.) loc 인덱서 또는 이때 인덱싱 값은 다음중 하나이다. 행 인덱싱값은 정수 또는 행 인덱스데이터 이고 열 인덱싱값은 라벨 문자열 이다. 인덱스데이터 인덱스데이터 슬라이스 인덱스데이터 리스트 같은 행 인덱스를 가지는 Boolean 시리즈 (행 인덱싱의 경우) 위의 값들을 반환하는 함수 글로 정의를 하자니 확 와닿지가 않는다. 데이터 프레임을 예로 들어 살펴보자. | index | A | B | C | D | |-------|----|----|----|----| | a | 10 | 11 | 12 | 13 | | b | 14 | 15 | 16 | 17 | | c | 18 | 19 | 20 | 21 | 인덱싱값을 하나만 받는 경우 만약 인덱서를 사용하면서 인덱스를 하나만 넣으면 행을 선택한다. 인덱스데이터가 "a"인 행을 고르면 행이 Series로 출력된다. Series로 항하로 길게 출력되긴하지만 데이터를 보면 행의 데이터를 가져오고 있다. A 10 B 11 C 12 D 13 Name: a, dtype: int32 인덱스데이터 슬라이스 | index | A | B | C | D | |-------|----|----|----|----| | b | 14 | 15 | 16 | 17 | | c | 18 | 19 | 20 | 21 | | index | A | B | C | D | |-------|----|----|----|----| | b | 14 | 15 | 16 | 17 | | c | 18 | 19 | 20 | 21 | 인덱스데이터의 리스트도 가능하다. | index | A | B | C | D | |-------|----|----|----|----| | b | 14 | 15 | 16 | 17 | | c | 18 | 19 | 20 | 21 | 인덱스를 가지는 Boolean Series도 행을 선택하는 인덱싱 값으로 쓸 수 있다. a False b False c True Name: A, dtype: bool | index | A | B | C | D | |-------|----|----|----|----| | c | 18 | 19 | 20 | 21 | 함수를 사용할 수도 있다. a False b False c True Name: A, dtype: bool | index | A | B | C | D | |-------|----|----|----|----| | c | 18 | 19 | 20 | 21 | 인덱서가 없는 경우에 사용했던 라벨 인덱싱이나 라벨 리스트 인덱싱은 불가능하다. 원래 행 인덱스값이 정수인 경우에는 슬라이싱도 라벨 슬라이싱 방식을 따른다. | index | A | B | C | D | |-------|----|----|----|----| | 0 | 10 | 11 | 12 | 13 | | 1 | 14 | 15 | 16 | 17 | | 2 | 18 | 19 | 20 | 21 | | 3 | 22 | 23 | 24 | 25 | | index | A | B | C | D | |-------|----|----|----|----| | 1 | 14 | 15 | 16 | 17 | | 2 | 18 | 19 | 20 | 21 | 위 내용을 표로 정리하면, | 인덱싱 값 | 가능 | 결과 | 자료형 | 추가사항 | | :------------------------- | :--: | :--: | :----------- | :-------------------------------------------------------- | | 행 인덱스값(정수) | O | 행 | 시리즈 | | | 행 인덱스값(정수) 슬라이스 | O | 행 | 데이터프레임 | 가 없는 경우와 같음 | | 행 인덱스값(정수) 리스트 | O | 행 | 데이터프레임 | | | Boolean Series | O | 행 | 데이터프레임 | 시리즈의 인덱스가 데이터프레임의 행 인덱스와 같아야 한다. | | Boolean Series 반환 함수 | O | 행 | 데이터프레임 | | | 열 라벨 | X | | | 가 없는 경우에만 쓸 수 있다. | | 열 라벨 리스트 | X | | | 가 없는 경우에만 쓸 수 있다. | 인덱싱값을 행과 열 모두 받는 경우 인덱싱값을 행과 열 모두 받으려면 와 같은 형태로 사용한다. 행 인덱스 라벨값이 , 열 인덱스 라벨값이 인 위치의 값을 구해보자. 10 인덱싱값으로 라벨 데이터의 슬라이싱 또는 리스트를 사용할 수도 있다. b 14 c 18 Name: A, dtype: int32 A 10 B 11 C 12 D 13 Name: a, dtype: int32 | index | B | D | |-------|----|----| | a | 11 | 13 | | b | 15 | 17 | 행 인덱스가 같은 Boolean Series 이러한 Boolean Series 반환하는 함수도 행의 인덱싱 값이 될 수 있다. | index | C | D | |-------|----|----| | b | 16 | 17 | | c | 20 | 21 | iloc 인덱서 인덱서는 인덱서와 반대로 라벨이 아니라 순서를 나타내는 정수 인덱스만 받는다. 다른 사항은 인덱서와 같다. 11 a 12 b 16 Name: C, dtype: int32 인덱서와 마찬가지로 인덱스가 하나만 들어가면 행을 선택한다. A 18 B 19 C 20 D 21 Name: c, dtype: int32 | index | A | B | C | D | |-------|----|----|----|----| | a | 10 | 11 | 12 | 13 | | b | 14 | 15 | 16 | 17 | | c | 72 | 76 | 80 | 84 | at, iat 인덱서 , 인덱서는 , 'iloc` 인덱서와 비슷하지만 하나의 스칼라 값을 뽑을 때만 사용한다. 하여 빠른 인덱싱 속도가 요구되는 경우에 사용한다. 5.62 µs ± 87.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 3.11 µs ± 29.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 16.8 µs ± 462 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 14.8 µs ± 262 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 출처 : 데이터사이언스 스쿨(datascienceschool.net)
2020년 10월 01일9분Pandas 데이터 입출력
Python데이터 입출력 Pandas는 데이터 파일을 읽어 데이터프레임으로 만들 수 있다. 다음처럼 여러 포맷을 지원한다. CSV EXCEL HTML JSON HDFS SAS STATA SQL 이중에서 가장 단순하지만 널리 사용되는 CSV(Comma Separated Values) 포맷 입출력에 대해 살펴보자. %%writefile 명령 샘플 데이터로 사용할 CSV 파일을 %%writefile 매직 명령으로 만들어보자. Overwriting sample1.csv CSV 파일 입력 | index | c1 | c2 | c3 | |-------|----|----|-----| | 0 | 1 | 1.11 | one | | 1 | 2 | 2.22 | two | | 2 | 3 | 3.33 | three | 위에서 읽은 데이터에는 열 인덱스는 있지만 행 인덱스 정보가 없으므로 0부터 시작하는 정수 인덱스가 자동으로 추가되었다. 만야그 위의 경우와 달리 데이터 파일에 열 인덱스 정보가 없는 경우에는 명령의 인수로 설정할 수 있다. Writing sample2.csv | index | c1 | c2 | c3 | |-------|----|----|-----| | 0 | 1 | 1.11 | one | | 1 | 2 | 2.22 | two | | 2 | 3 | 3.33 | three | 만약 테이블 내의 특정한 열을 행 인덱스로 저정하고 싶으면 인수를 사용한다. | c1 | c2 | c3 | |----|----|-----| | 1 | 1.11 | one | | 2 | 2.22 | two | | 3 | 3.33 | three | 확장자가 csv가 아닌 파일 즉, 데이터를 구분하는 구분자가 쉼표(Comma)가 아니면 인수를 써서 구분자를 지정해준다. 예를들면 구분자의 길이가 정해지지 않은 공백인 경우 정규식 문자열을 사용할 수 있다. Writing sample3.txt | index | c1 | c2 | c3 | c4 | |-------|------|--------|--------|--------| | 0 | 0.179181 | -1.538472 | 1.347553 | 0.43381 | | 1 | 1.024209 | 0.087307 | -1.281997 | 0.49265 | | 2 | 0.417899 | -2.002308 | 0.255245 | -1.10515 | 자료 파일중에 건너뛰어야 할 행이 있다면 인수를 사용한다. Writing sample4.txt | index | c1 | c2 | c3 | |-------|----|----|-----| | 0 | 1 | 1.11 | one | | 1 | 2 | 2.22 | two | | 2 | 3 | 3.33 | three | 특정한 값을 NaN으로 취급하고 싶으면 인수에 NaN값으로 취급할 값을 넣는다. Writing sample5.csv | index | c1 | c2 | c3 | |-------|----|----|-----| | 0 | 1.0 | 1.11 | one | | 1 | 2.0 | | two | | 2 | NaN | 3.33 | three | names : 열인덱스명이 없는경우 지정 가능 (,names=[['c1', 'c2', 'c3']]) sep : 데이터 구분자 지정 (,sep='\s+') skiprows : 행 건너뛰기 (,skiprows=[[0, 1]]) navalues : 특정값을 NaN값으로 대체 (,navalues=[['누락']]) CSV 파일 출력 ,c1, c2, c3 0,1.0, 1.11, one 1,2.0, , two 2,, 3.33, three 파일을 읽을때와 마찬가지로 출력할 때도 seq인수로 구분자를 바꿀 수 있다. 또 인수로 NaN 표시값을 바꿀 수도 있다. ,c1, c2, c3 0,1.0, 1.11, one 1,2.0, , two 2,누락, 3.33, three , 인수를 지정하여 인덱스 및 해더 출력 여부를 저정하는 것도 가능하다. | index | c1 | c2 | c3 | |-------|----|----|-----| | a | 1.0 | 1.11 | one | | b | 2.0 | | two | | c | NaN | 3.33 | three | 출처 : 데이터사이언스 스쿨(datascienceschool.net)
2020년 09월 27일6분Pandas 소개
PythonPandas 패키지 소개 Pandas 패키지는 데이터 분석을 할 때 가장 많이 쓰이는 패키지이다. 대부분의 데이터는 시계열(series)이나 표(table)의 형태로 나타낼 수 있는데 Pandas에서는 이러한 표 데이터를 다루기 위한 시리즈(Series) 클래스와 데이터프레임(DataFrame) 클래스를 제공한다. Series Series는 numpy에서 제공하는 1차원 배열과 비슷하지만 각 데이터의 의미를 표시하는 인덱스(index)를 붙일 수 있다. 데이터 자체는 값(Value)라고 한다. > Series = Value(값) + Index(인덱스) Series 생성 서울 9904312 부산 3448737 인천 2890451 대구 2466052 dtype: int64 만약 인덱스 지정 없이 Series를 만들면 Series의 인덱스는 0부터 시작하는 정수값이 된다. 0 10 1 11 2 12 3 13 dtype: int64 Index(['서울', '부산', '인천', '대구'], dtype='object') array([9904312, 3448737, 289511, 264544], dtype=int64) > - : Series 데이터의 이름 > - : Series 인덱스의 이름 도시 서울 9904312 부산 3448737 인천 289511 대구 264544 Name: 인구, dtype: int64 Series 연산 연산은 Series의 값에만 적용되며 인덱스 값은 변하지 않는다. 서울 9.904312 부산 3.448737 인천 2.890451 대구 2.466052 dtype: float64 Series 인덱싱 인덱스 라벨을 이용한 인덱싱 또는 배열 인덱싱을 이용한 슬라이싱이 가능하다. (3448737, 3448737) 배열 인덱싱을 하면 자료의 순서를 바꾸거나 특정 라벨의 자료만 선택할 수 있다. 서울 9904312 대구 2466052 dtype: int64 부산 3448737 인천 2890451 dtype: int64 서울 9904312 부산 3448737 인천 2890451 대구 2466052 dtype: int64 부산 3448737 인천 2890451 dtype: int64 만약 라벨 값이 영문인 경우에는 마치 속성처럼 점(.)을 이용하여 접근할 수도 있다. a 0 b 1 c 2 dtype: int64 (0, 1) Series와 Dictionary 자료형 Series 객체는 라벨 값에 의해 인덱싱이 가능하므로 실질적으로 라벨 값을 키(key)로 가지는 Dictionary 자료형과 같다고 볼수 있다. 따라서 Dictionary 자료형에서 제공하는 in 연산도 가능하고 items 메서드를 이용하면 for 루프를 통해 각 원소의 key와 value에 접근할 수도 있다. True False 서울 = 9904312 부산 = 3448737 인천 = 2890451 대구 = 2466052 또 Dictionary 객체에서 시리즈를 만들수 있다. 서울 9631482 부산 3393191 인천 2632035 대전 1490158 dtype: int64 Dictionary의 원소는 순서를 가지지 않으므로 Series의 데이터에도 순서가 보장되지 않는다. 만약 순서를 정하고 싶다면 인덱스를 리스트로 지정해야 한다. 부산 3393191 서울 9631482 인천 2632035 대전 1490158 dtype: int64 인덱스 기반 연산 s : 2015년 인구 증가 s2 : 2010년 인구 증가 대구 NaN 대전 NaN 부산 55546.0 서울 272830.0 인천 258416.0 dtype: float64 array([ 6511121, -6182745, 258416, 975894], dtype=int64) 대구와 대전의 경우 s, s1 모두에 준재하지 않기 때문에 계산이 불가능하므로 (Not a Number)값을 가지게 된다. 또한 값이 자료형에서만 가능하므로 다른 계산 결과도 모두 자료형이 되었다. 이 아닌 값을 구하려면 메서드를 사용하야 한다. 대구 False 대전 False 부산 True 서울 True 인천 True dtype: bool 부산 55546.0 서울 272830.0 인천 258416.0 dtype: float64 인구 증가율(%)은 다음과 같이 구할 수 있다. 부산 1.636984 서울 2.832690 인천 9.818107 dtype: float64 데이터 갱신, 추가, 삭제 인덱싱을 이용하면 딕셔너리처럼 데이터를 갱신하거나 추가 할 수 있다. 부산 1.630000 서울 2.832690 인천 9.818107 dtype: float64 부산 1.630000 서울 2.832690 인천 9.818107 대구 1.410000 dtype: float64 부산 1.630000 인천 9.818107 대구 1.410000 dtype: float64 DataFrame 시리즈가 1차원 벡터 데이터에 행방향 인덱스(row index)를 붙인 것이라면 데이터프레임 클래스는 2차원 행렬 데이터에 인덱스를 붙인 것과 비슷하다. 2차원이므로 각각의 행 데이터의 이름이 되는 행방향 인덱스(row index) 뿐 아니라 각각의 열 데이터의 이름이 되는 열방향 인덱스(column index)도 붙일 수 있다. DataFrame 생성 1. 하나의 열이 되는 데이터를 리스트나 일차원 배열로 준비 2. 이 각각의 열에 대한 이름(라벨)을 키로 가지는 딕셔너리 생성 3. 이 데이터를 DataFrame 클래스 생성자에 넣고 열방향 인덱스는 , 행방행 인덱스는 인수로 지정 | | 지역 | 2015 | 2010 | 2005 | 2000 | 2010-2015 증가율 | |-|-|-|-|-|-|-| | 서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | 0.0283 | | 부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | 0.0163 | | 인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 0.0982 | | 대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | 0.0141 | 앞에서 데이터프레임은 2차원 배열 데이터를 기반으로 한다고 했지만 사실은 공통 인덱스를 가지는 열 시리즈(column series)를 딕셔너리로 묶어놓은 것이라고 보는것이 더 정확하다. 2차원 배열 데이터는 모든 원소와 같은 자료형을 가져야 하지만 데이터프레임은 각 열(column)마다 자료형이 다를 수 있기 때문이다. 위 예제에서도 지역과 인구와 증가율은 각각 문자열, 정수, 부동소수점 실수 이다. 시리즈와 마찬가지로 데이터만 접근하려면 속성을 사용하고, 열방향 인덱스와 행방향 인덱스는 각각 , 속성으로 접근한다. array([['수도권', 9904312, 9631482, 9762546, 9853972, 0.0283], ['경상권', 3448737, 3393191, 3512547, 3655437, 0.0163], ['수도권', 2890451, 2632035, 2517680, 2466338, 0.0982], ['경상권', 2466052, 2431774, 2456016, 2473990, 0.0141]], dtype=object) Index(['지역', '2015', '2010', '2005', '2000', '2010-2015 증가율'], dtype='object') Index(['서울', '부산', '인천', '대구'], dtype='object') 시리즈에서 처럼 열방향 인덱스와 행방향 인덱스에 이름을 붙일수 있다. | 특성\도시 | 지역 | 2015 | 2010 | 2005 | 2000 | 2010-2015 증가율 | |-|-|-|-|-|-|-| | 서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | 0.0283 | | 부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | 0.0163 | | 인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 0.0982 | | 대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | 0.0141 | 데이터 프레임은 전치(transpose)를 포함하여 2차원 numpy 배열이 가지는 대부분의 속성이나 메서드를 지원한다. | 도시\특성 | 서울 | 부산 | 인천 | 대구 | |-|-|-|-|-| | 지역 | 수도권 | 경상권 | 수도권 | 경상권 | | 2015 | 9904312 | 3448737 | 2890451 | 2466052 | | 2010 | 9631482 | 3393191 | 2632035 | 2431774 | | 2005 | 9762546 | 3512547 | 2517680 | 2456016 | | 2000 | 9853972 | 3655437 | 2466338 | 2473990 | | 2010-2015 증가율 | 0.0283 | 0.0163 | 0.0982 | 0.0141 | 열 데이터의 갱신, 추가, 삭제 데이터프레임은 열 시리즈의 딕셔너리로 볼수 있으므로 열 단위로 데이터를 갱신하거나 추가, 삭제할 수 있다. | | 지역 | 2015 | 2010 | 2005 | 2000 | 2010-2015 증가율 | |-|-|-|-|-|-|-| | 서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | 2.83 | | 부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | 1.63 | | 인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 9.82 | | 대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | 1.41 | | | 지역 | 2015 | 2010 | 2005 | 2000 | 2010-2015 증가율 | 2005-2010 등가율 | |-|-|-|-|-|-|-|-| | 서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | 2.83 | -1.34 | | 부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | 1.63 | -3.40 | | 인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 9.82 | 4.54 | | 대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | 1.41 | -0.99 | | | 지역 | 2015 | 2010 | 2005 | 2000 | 2005-2010 등가율 | |-|-|-|-|-|-|-| | 서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | -1.34 | | 부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | -3.40 | | 인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 4.54 | | 대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | -0.99 | 열 인덱싱 데이터프레임은 열 시리즈의 딕셔너리와 비슷하다고 하였다. 따라서 데이터프레임을 인덱싱 할 때도 열 라벨(column label)을 키값으로 생각하여 인덱싱을 할 수 있다. 인덱스로 라벨을 넣거나 배열형태로 넣는것도 가능하다. 서울 수도권 부산 경상권 인천 수도권 대구 경상권 Name: 지역, dtype: object pandas.core.series.Series | | 2010 | 2015 | |-|-|-| | 서울 | 9631482 | 9904312 | | 부산 | 3393191 | 3448737 | | 인천 | 2632035 | 2890451 | | 대구 | 2431774 | 2466052 | 만약 하나의 열만 빼서 데이터프레임 자료형을 유지하고 싶다면 원소가 하나인 리스트를 써서 인덱싱 하면된다. | | 2010 | |-|-| | 서울 | 9631482 | | 부산 | 3393191 | | 인천 | 2632035 | | 대구 | 2431774 | 서울 9631482 부산 3393191 인천 2632035 대구 2431774 Name: 2010, dtype: int64 데이터프레임의 열 인덱스가 문자열 라벨을 가지고 있는 경우에는 순서를 나타내는 정수 인덱스를 열 인덱싱에 사용할 수 없다. 다만 원래부터 문자열이 아닌 정수형 열 인덱스를 가지는 경우에는 인덱스 값으로 정수를 사용할 수 있다. | | 0 | 1 | 2 | 3 | |-|-|-|-|-| | 0 | 0 | 1 | 2 | 3 | | 1 | 4 | 5 | 6 | 7 | | 2 | 8 | 9 | 10 | 11 | 0 2 1 6 2 10 Name: 2, dtype: int32 | | 1 | 2 | |-|-|-| | 0 | 1 | 2 | | 1 | 5 | 6 | | 2 | 9 | 10 | 행 인덱싱 만약 행 단위로 인덱싱을 하고자 하면 항상 슬라이싱(slicing)을 해야 한다. 인덱스의 값이 문자 라벨이면 라벨 슬라이싱도 가능하다. | | 지역 | 2015 | 2010 | 2005 | 2000 | 2005-2010 등가율 | |-|-|-|-|-|-|-| | 서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | -1.34 | | | 지역 | 2015 | 2010 | 2005 | 2000 | 2005-2010 등가율 | |-|-|-|-|-|-|-| | 부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | -3.4 | | | 지역 | 2015 | 2010 | 2005 | 2000 | 2005-2010 등가율 | |-|-|-|-|-|-|-| | 서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | -1.34 | | 부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | -3.40 | 개별 데이터 인덱싱 데이터프레임에서 열 라벨로 시리즈를 인덱싱하면 시리즈가 된다. 이 시리즈를 다시 행 라벨로 인덱싱하면 개발 데이터가 나온다. 9904312 지금까지의 데이터프레임 인덱싱 방법을 정리하면 다음과 같다. | 인덱싱값 | 가능 | 결과 | 자료형 | 추가사항 | | :-------------------------- | :--: | :--: | :----------- | :----------------------------------------- | | 라벨 | O | 열 | 시리즈 | | | 라벨 리스트 | O | 열 | 데이터프레임 | | | 인덱스데이터(정수) | X | | | 열 라벨이 정수인 경우 라벨 인덱싱으로 인정 | | 인덱스데이터(정수 슬라이스) | O | 행 | 데이터프레임 | | 출처 : 데이터사이언스 스쿨(datascienceschool.net)
2020년 09월 26일17분😄 NLP - 한국어 영화 리뷰 감정분석
Ai한국어 데이터에 대해서 텍스트 분석을 해보자. 아래 데이터는 한국어 분석 학습을 위해 다양한 방식으로 사용되고 있다. 여기서는 한글 분석을 위해 Konlpy를 사용하고, 텐서플로 케라스를 이용해 모델을 만들도록 하겠다. 데이터셋 : Naver sentiment movie corpus (다운로드 링크 : https://github.com/e9t/nsmc/) NSMC 약어까지 사용할 정도로 많이들 사용하는 데이터 인듯 싶다. 데이터 설명 영화 리뷰 중 영화당 100개의 리뷰이고 총 200,000개의 리뷰(train:15만, test:5만) 1점 ~ 10점 까지의 평점 중에서 중립적인 평점(5점~8점)을 제외하고 분류를 하였다. 부정 : 1점 ~ 4점 긍정 : 9점 ~ 10점 칼람정보: id, document, label id: 리뷰 아이디 document: 리뷰 내용 label: 레이블 (0: negative, 1: positive) 각 파일에 대한 리뷰 갯수 ratings.txt: All 20만 ratings_test.txt: 5만 ratings_train.txt: 15만 모든 리뷰텍스트는 140자 이내이고, 각 감정 분류는 동일하게 샘플링 된다.(i.e., random guess yields 50% accuracy) 10만개의 부정적인 리뷰 10만개의 긍정적인 리뷰 중립적인 리뷰는 제외 데이터 준비 다운로드 받은 데이터를 pandas를 이용해 읽어보자. 필드 구문이 탭으로 되어 있기 때문에 로 구분자를 지정해주어야 한다. 데이터 전처리 데이터를 학습 시키기 위해 전처리를 진행해야 하는데, Konlpy를 이용해 을 하도록 하자. 영어의 경우 주어진 단어의 빈도만을 사용해서 처리해도 크게 문제는 없지만 한국어는 영어와는 달리 띄어쓰기로 의미를 구분짓기에는 한계가 있고, 리뷰 특성상 맞춤법이나 띄어쓰기가 제대로 되어있지 않는 경우가 있을 수 있기 때문에 정확한 분류를 위해서는 Konlpy를 이용하는 것이 좋다. >Konlpy는 띄어쓰기 알고리즘과 정규화를 이용해서 맞춤법이 틀린 문장도 어느 정도 고쳐주면서 형태소 분석과 품사를 태깅해주는 여러 클래스를 제공하고 있다.^^! [('흔들리는', 'Verb'), ('꽃', 'Noun'), ('들', 'Suffix'), ('속', 'Noun'), ('에서', 'Josa'), ('네', 'Noun'), ('샴푸', 'Noun'), ('향', 'Noun'), ('이', 'Josa'), ('느껴진거야', 'Verb')] 테스트 삼아 간단한 문장을 넣고 확인 해보면 이런 형태로 분리를 해주는 것을 알수 있다. 토크나이즈 함수를 만들어 사용하도록 한다. >norm은 정규화, stem은 근어로 표시하기를 나타냄 리뷰가 null인 경우 위 위 함수에서 오류가 발생할 수 있으니 사전에 null값 확인해보고 빈민자열로 대체하자! 이제 학습데이터와 테스트데이터를 분석하여 저장해두자. 분석결과가 끝났으면 다음과 같은 형태로 데이터가 변형 되었을 것이다. (['아/Exclamation', '더빙/Noun', '../Punctuation', '진짜/Noun', '짜증나다/Adjective', '목소리/Noun'], 0) (['굳다/Adjective', 'ㅋ/KoreanParticle'], 1) 15만 학습데이터에 분리된 토큰 개수를 살펴보자. 토큰개수: 2159921 이제 이데이터를 가지고 nltk를 이용해 전처리를 한다. 클래스는 문서를 편리하게 탐색할 수 있는 다양한 기능을 제공하고 있다. 여기서는 매서드를 이용해 데이터가 가장 자주 사용되는 단어를 가져올 때 사용하겠다. 2159921 49894 [('./Punctuation', 67778), ('영화/Noun', 50818), ('하다/Verb', 41209), ('이/Josa', 38540), ('보다/Verb', 38538), ('의/Josa', 30188), ('../Punctuation', 29055), ('가/Josa', 26627), ('에/Josa', 26468), ('을/Josa', 23118)] 데이터 탐색 출력빈도가 높은 상위 토큰 10개를 matplotlib을 이용해 그래프로 확인해보자. !png 모델을 만들기 위해 백터화를 해야 하는데, 자주 사용되는 토큰 10000개를 사용해 데이터를 백터화 하자.(원 핫 인코딩 대신 CountVectorization을 사용) 문서 집합에서 단어 토큰을 생성하고 각 단어의 수를 세어 BOW(Bag of Words) 인코딩한 벡터를 만드는 역할을 한다. 시간이 오래 걸리므로 100개만 해보자... 이 과정은 데이터 양이 큰 만큼 시간이 오래 걸리기 때문에 이 작업을 반복하지 않도록 태깅을 마친 후에는 json파일로 저장하는 것도 좋은 방법이다. 문서에서 상위로 선택된 단어들중 몇개가 포함이 되는지를 알아야 한다. 이렇게 하면 x축 데이터에는 단어들이 빈도수 정보?, y축에는 분류 결과를 깔끔하게 정리할 수 있다. 이제 데이터를 float로 형 변환 시켜주면 데이터 전처리 과정은 끝~~ 데이터 모델링 텐서플로 케라스를 이용해 모델을 만들어 보자. 레이어 구성은 두개의 Danse층은 64개의 유닛을 가지고 활성함수는 relu를 사용하고, 마지막층은 sigmoid 활성화 함수를 사용해 긍정 리뷰일 확률을 출력할 것이다. 손실 함수는 binary_crossentropy, RMSprop 옵티마이저를 통해 경사하강법을 진행 배치 사이즈는 512, 에포크는 10번으로 학습 자, 이제 학습을 시켜 모델을 만들어 보자! 먼가 있어 보이는 진행률 상태를 볼 수 있다.:) WARNING:tensorflow:From C:\Users\DESKTOP\.conda\envs\nlp\lib\site-packages\tensorflow\python\ops\mathops.py:3066: toint32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.cast instead. Epoch 1/10 150000/150000 [==============================] - 17s 115us/sample - loss: 0.5611 - binaryaccuracy: 0.6948s - loss: 0.6134 - binaryaccuracy: - ETA: 21s - loss: 0.6108 - binaryaccuracy: 0. - ETA: 20s - - ETA: 15s - loss: 0.5957 - binaryaccuracy: 0.67 - ETA: 15s - loss: 0.5955 - binaryaccuracy: 0.67 - ETA: 15s - loss: 0.5951 - binaryaccura - ETA: 14s - loss: 0.5930 - binaryaccuracy: 0. - ETA: 14s - loss: 0.5923 - binaryaccuracy: - ETA: 13s - loss: 0.5911 - binaryaccura - ETA: 8s - loss: 0.5785 - binaryaccuracy: 0. - ETA: 8s - loss: 0.5771 - binaryaccuracy: 0.68 - ETA: 8s - loss: 0.5764 - binaryaccuracy: - ETA: 7s - loss: 0.5751 - binaryaccura - ETA: 6s - loss: 0.5727 - binaryaccuracy: - ETA: 6s - loss: 0.5713 - binaryaccuracy: 0.6 - ETA: 5s - loss: 0.5708 - binarya - ETA: 4s - loss: 0.5685 - binaryaccuracy: - ETA: 4s - loss: 0.5676 - binaryaccuracy: 0.691 - ETA: 4s - loss: 0.5674 - binarya - ETA: 2s - loss: 0.5652 - binaryaccuracy: 0.692 - ETA: 2s - loss: 0.5651 - binaryaccurac - ETA: 1s - loss: 0.5639 - binaryaccuracy - ETA: 1s - loss: 0.5628 - binaryaccuracy: 0.694 - ETA: 1s - loss: 0.5627 - binaryaccuracy: 0.694 - ETA: 1s - loss: 0.5626 - binaryaccuracy: 0.694 - ETA: 1s - loss: 0.5623 - binaryaccuracy: 0.694 - ETA: 1s - loss: 0.5623 - binaryaccuracy: - ETA: 0s - loss: 0.5615 - binaryaccuracy: 0.69 - ETA: 0s - loss: 0.5612 - binaryaccuracy: 0.694 - ETA: 0s - loss: 0.5613 - binaryaccuracy: 0.6 Epoch 2/10 150000/150000 [==============================] - 12s 83us/sample - loss: 0.5313 - binaryaccuracy: 0.71254s - loss: 0.5221 - binaryac - ETA: 15s - loss: 0.5345 - binaryaccuracy: - ETA: 16s - loss: 0.5377 - binaryaccuracy - ETA: 14s - loss - ETA: 13s - lo - ETA: 7s - loss: 0.5341 - binaryaccuracy: 0.71 - ETA: 7s - - ETA: 4s - loss: 0.5340 - binaryaccur - ETA: 3s - loss: 0.5335 - binaryaccuracy: 0.71 - ETA: 3s - loss: 0.5335 - binary - ETA: 2s - loss: 0.5325 - binary_accuracy: 0 - ETA: 1s - loss: 0. Epoch 3/10 150000/150000 [==============================] - 13s 86us/sample - loss: 0.5236 - binaryaccuracy: 0.7170s - loss: 0.5284 - binaryaccuracy - ETA: 10s - los - ETA: 9s - loss: 0.5255 - binaryaccurac - ETA: 9s - loss: 0.5250 - binaryaccuracy: 0. - ETA: 9s - loss: 0.5255 - binaryaccuracy: 0.716 - ETA: 9s - loss: 0.5254 - binary - ETA: 8s - loss: 0.5247 - binaryacc - ETA: 7s - loss: 0.5251 - binaryaccuracy: 0.7 - ETA: 7s - loss: 0.5252 - binaryaccuracy: 0.71 - ETA: 7s - loss: 0.5249 - binaryaccuracy: 0.716 - ETA: 7s - loss: 0.5248 - binaryaccuracy: 0.7 - ETA: 6s - loss: 0.5245 - binaryaccuracy: - ETA: 6s - loss: 0.5246 - binaryaccuracy: 0 - ETA: 5s - loss: 0.5247 - bina - ETA: 5s - loss: 0.5249 - b - ETA: 3s - loss: 0.5246 - binaryac - ETA: 2s - loss: 0.5246 - binaryac - ETA: 2s - loss: 0.5244 - binaryaccuracy: 0.71 - ETA: 2s - loss: 0.5244 - binaryaccuracy: - ETA: 1s - loss: 0.5242 - binaryaccur - ETA: 1s - loss: 0.5243 - binaryaccuracy: 0 - ETA: 0s - loss: 0.5243 - binaryaccuracy - ETA: 0s - loss: 0.5239 - binaryaccuracy: 0.7 - ETA: 0s - loss: 0.5237 - binaryaccuracy: 0.7 - ETA: 0s - loss: 0.5236 - binaryaccuracy: 0.71 Epoch 4/10 150000/150000 [==============================] - 13s 89us/sample - loss: 0.5179 - binaryaccuracy: 0.7219s - loss: 0.5201 - binaryaccuracy: 0 - ETA: 8s - loss: 0.5211 - binarya - ETA: 9s - loss: 0.5208 - binaryaccuracy: 0.721 - ETA: 8s - loss: 0.5201 - binaryaccuracy: 0.72 - ETA: 8s - loss: 0.5210 - - ETA: 7s - loss: 0.5180 - binaryac - ETA: 7s - loss: 0.5187 - binar - ETA: 6s - loss: 0.5186 - binaryaccura - ETA: 6s - loss: 0.5183 - binaryaccuracy: 0.7 - ETA: 6s - loss: 0.5182 - binaryac - ETA: 5s - loss: 0.5179 - binaryaccuracy: 0.723 - ETA: 5s - loss: 0.5179 - binaryaccurac - ETA: 4s - loss: 0.5187 - binaryaccuracy: 0.722 - ETA: 4s - lo Epoch 5/10 150000/150000 [==============================] - 13s 87us/sample - loss: 0.5132 - binaryaccuracy: 0.72531s - loss: 0.5093 - binaryaccuracy: 0.72 - ETA: 11s - ETA: 9s - loss: 0.5156 - binaryaccuracy: 0.7 - ETA: 9s - loss: 0.5163 - binaryaccuracy: - ETA: 9s - loss: 0.5154 - binaryaccurac - ETA: 8s - loss: 0.5164 - bin - ETA: 7s - loss: 0.5156 - binary - ETA: 6s - loss: 0.5168 - binaryaccuracy: 0. - ETA: 6s - loss: 0.5163 - binaryaccuracy: 0.72 - ETA: 6s - loss: 0.5160 - binaryaccur - ETA: 5s - loss: 0.5154 - binaryaccuracy: 0.723 - ETA: 5s - loss: 0.5153 - binarya - ETA: 4s - los - ETA: 2s - loss: 0.5136 - binaryacc - ETA: 1s - loss: 0.5135 - binaryaccuracy: 0.724 - ETA: 1s - loss: 0.5134 - binaryaccuracy - ETA: 1s - loss: 0.5132 - binaryac Epoch 6/10 150000/150000 [==============================] - 13s 87us/sample - loss: 0.5094 - binaryaccuracy: 0.72850s - loss: 0.4971 - binaryaccuracy: 0.73 - ETA: 11s - loss: 0.5000 - binary - ETA: 9s - loss: 0.5043 - binaryaccuracy: - ETA: 10s - loss: 0.5081 - binaryaccuracy: - ETA: 9s - loss: 0.5086 - binarya - ETA: 9s - loss: 0.5106 - binaryaccuracy: 0 - ETA: 9s - loss: 0.5111 - binaryaccuracy: 0.72 - ETA: 9s - loss: 0.5112 - binaryaccuracy: - ETA: 9s - loss: 0.5122 - binaryaccuracy: 0.724 - ETA: 9s - loss: 0.5122 - binaryaccuracy: - ETA: 8s - loss: 0.5114 - binaryaccuracy: 0. - ETA: 8s - loss: 0.5115 - binaryaccuracy: 0. - ETA: 8s - loss: 0.5118 - binaryaccuracy: 0 - ETA: 8s - loss: 0.5111 - binaryaccuracy: 0.725 - ETA: 8s - loss: 0.5114 - - ETA: 6s - loss: 0.5099 - binaryaccuracy - ETA: 6s - loss: 0.5108 - binaryaccuracy - ETA: 5s - loss: 0.5100 - binaryaccuracy: 0.726 - ETA: 5s - loss: 0.5102 - binaryacc - ETA: 4s - loss: 0.5102 - binaryaccurac - ETA: 4s - loss: 0.5102 - binaryaccuracy: 0.7 - ETA: 4s - l - ETA: 1s - loss: 0.5102 - binaryaccu - ETA: 1s - loss: 0.5098 - bi - ETA: 0s - loss: 0.5094 - binary_accuracy: 0.72 Epoch 7/10 150000/150000 [==============================] - 12s 79us/sample - loss: 0.5064 - binaryaccuracy: 0.73061s - loss: 0.5050 - binaryaccura - ETA: 9s - loss: 0.5096 - binaryaccuracy: 0.72 - ETA: 9s - loss: 0.5090 - binaryaccuracy - ETA: 8s - loss: 0.5083 - binaryaccuracy: 0.7 - ETA: 8s - loss: 0.5082 - binaryaccuracy: 0.7 - ETA: 8s - loss: 0.5083 - binaryaccur - ETA: 7s - loss: 0.5085 - binaryaccuracy: 0 - ETA: 6s - loss: 0.5079 - binarya - ETA: 5s - loss: 0.5079 - binaryaccuracy: 0. - ETA: 5s - loss: 0.5080 - binaryaccuracy: - ETA: 5s - loss: 0.5078 - binaryaccuracy - ETA: 4s - loss: 0.5081 - binaryaccuracy: 0 - ETA: 4s - loss: 0.5080 - binaryaccuracy: 0.730 - ETA: 4s - loss: 0.5080 - binaryaccuracy: 0.730 - ETA: 4s - loss: 0.5078 - binaryaccuracy: - ETA: 4s - loss: 0.5072 - binaryaccuracy: 0 - ETA: 3s - loss: 0.5072 - binary - ETA: 2s - lo - ETA: 0s - loss: 0.5064 - binary_accuracy: 0.730 Epoch 8/10 150000/150000 [==============================] - 13s 85us/sample - loss: 0.5037 - binaryaccuracy: 0.73210s - loss: 0.5053 - binaryacc - ETA: 9s - loss: 0.5045 - binaryaccuracy: - ETA: 9s - loss: 0.5024 - binaryaccu - ETA: 8s - loss: 0.5013 - binaryaccu - ETA: 8s - loss: 0.5014 - binaryaccuracy: 0.73 - ETA: 8s - loss: 0.5007 - binar - ETA: 6s - loss: 0.5013 - binarya - ETA: 6s - loss: 0.5016 - binaryaccuracy: - ETA: 6s - loss: 0.5019 - binaryaccuracy: 0.73 - ETA: 5s - loss: 0.5019 - binaryaccuracy: 0 - ETA: 5s - loss: 0.5023 - binaryaccuracy: 0.73 - ETA: 5s - loss: 0.5021 - binaryaccuracy: 0.73 - ETA: 5s - l - ETA: 3s - loss: 0.5027 - ETA: 2s - loss: 0.5036 - binaryaccuracy: - ETA: 2s - loss: 0.5033 - binaryaccuracy: 0.732 - ETA: 2s - loss: 0.5033 - binaryaccuracy: 0.73 - ETA: 2s - loss: 0.5034 - binaryaccur - ETA: 1s - loss: 0.5035 - binaryaccuracy: - ETA: 0s - loss: 0.5033 - binaryaccuracy: - ETA: 0s - loss: 0.5035 - binary_acc Epoch 9/10 150000/150000 [==============================] - 13s 85us/sample - loss: 0.5015 - binaryaccuracy: 0.7337s - loss: 0.5023 - binaryaccu - ETA: 13s - loss: 0.4956 - binaryaccuracy: 0. - ETA: 14s - loss: 0.4949 - binaryaccu - ETA: 15s - loss: 0.4938 - binaryaccuracy: 0. - ETA: 15s - loss: 0.4977 - binaryaccuracy: 0.73 - ETA: 15s - loss: 0.4981 - binaryaccuracy: 0. - ETA: 14s - loss: - ETA: 12s - loss: 0.4989 - binaryaccuracy - ETA: 12s - loss: 0.4992 - binaryaccuracy - ETA: 11s - loss: 0.4990 - binary - ETA: 10s - loss: 0.4982 - binaryaccuracy: 0.73 - ETA: 10s - loss: 0.4982 - binar - ETA: 9s - loss: 0.4997 - binaryaccuracy: 0. - ETA: 9s - loss: 0.4997 - binaryaccuracy: 0 - ETA: 8s - loss: 0.4996 - binaryaccuracy: 0.7 - ETA: 8s - loss: 0.4997 - binary - ETA: 7s - loss: 0.5011 - bina - ETA: 6s - loss: 0.5014 - binaryaccuracy: 0.7 - ETA: 6s - loss: 0.5011 - binary - ETA: 4s - loss: 0.5016 - binaryaccuracy: 0.733 - ETA: 4s - loss: 0.5017 - binaryaccurac - ETA: 4s - loss: 0.5018 - binaryac - ETA: 2s - loss: 0.5022 - binaryaccuracy: 0.7 - ETA: 2s - loss: 0.5023 - binaryaccuracy: 0.73 - ETA: 2s - loss: 0.5021 - binaryaccuracy: - ETA: 1s - loss: 0.5018 - binaryaccuracy: 0 - ETA: 1s - loss: 0.5019 - binaryaccuracy - ETA: 1s - loss: 0.5018 - binaryaccuracy: 0.73 - ETA: 1s - loss: 0.5018 - binaryaccuracy: 0.7 - ETA: 1s - loss: 0.5017 - binary_a Epoch 10/10 150000/150000 [==============================] - 14s 91us/sample - loss: 0.4995 - binaryaccuracy: 0.73620s - loss: 0.4948 - binaryaccuracy: - ETA: 14s - loss: 0.4965 - binaryaccura - ETA: 14s - loss: 0.4980 - binaryaccu - ETA: 12s - loss: 0.4973 - binaryaccuracy - ETA: 12s - loss: 0.5014 - binar - ETA: 12s - loss: 0.4992 - binaryaccuracy - ETA: 12s - loss: 0.4996 - binaryaccuracy: - ETA: 12s - loss: 0.5001 - binaryaccuracy: 0.73 - ETA: 12s - loss: 0.5003 - binaryaccura - ETA: 11s - loss: 0.5011 - b - ETA: 9s - loss: 0.5027 - binaryaccuracy: - ETA: 9s - loss: 0.5026 - binaryaccuracy - ETA: 8s - loss: 0.5013 - binaryaccur - ETA: 8s - loss: - ETA: 6s - loss: 0.5013 - binary - ETA: 5s - loss: 0.5006 - binaryac - ETA: 4s - loss: 0.4997 - binaryaccurac - ETA: 4s - loss: 0.5001 - binaryac - ETA: 3s - loss: 0.5001 - b - ETA: 1s - loss: 0.4989 - binaryaccuracy: 0.736 - ETA: 1s - loss: 0.4989 - binaryaccuracy: 0.736 - ETA: 1s - loss: 0.4988 - binaryaccuracy: 0.736 - ETA: 1s - loss: 0.4989 - binaryaccurac - ETA: 1s - loss: 0.4991 - binaryaccuracy: 0. - ETA: 0s - loss: 0.4993 - binaryaccura - ETA: 0s - loss: 0.4995 - binary_accuracy: 0.73 모델 평가 학습데이터를 이용해 모델 학습이 끝났다면 테스트 데이터를 가지고 모델을 평가해보자. 50000/50000 [==============================] - 12s 234us/sample - loss: 0.5198 - binaryaccuracy: 0.7184s - loss: 0.5197 - b - ETA: 11s - loss: 0.5199 - binaryaccuracy: 0. - ETA: 1s - loss: 0.5198 - b [0.5197769028568268, 0.71842] >여기서는 100건으로 했기때문에 좀 낮은 71%의 정확도가 나왔다. 아마 사용한 토큰수를 100개가 아닌 10000개로 했다면 85%정도의 정확도를 확인할 수 있을 것이다. 팁으로 힘들게 만든 모델을 아래와 같이 저장해두고 나중에 로드해서 사용할수 있으니 꼭 알아두자. 결과 예측하기 이제 리뷰 문자열을 받아 바로 결과를 예측하는 함수를 만들어 보자 데이터 형태를 맞추기 위해 np.expand_dims 매서드를 이용해 array의 축을 확장 시켜주어야 한다. 최종 확률이 0.5 이상이면 긍정, 그렇지 않으면 부정이라고 예측을 하겠다. 대략 테스트를 먼저 해보고... ['아주/Noun', '재미/Noun', '있다/Adjective'] 0.9102853536605835 테스트한 로직을 함수화해서 사용하자. 재미 정말 없어요 ==> 부정 (93%) 이제 리뷰텍스트 만으로 긍정인지 혹은 부정인지를 어느정도 판단 할 수 있게 되었다. 지금까지 영화리뷰 데이터를 통해서 감정분석을 해보았는데 상품, 게임, 음식등의 사용자 의견이 담긴 데이터를 잘 모아서 활용한다면 다양한 곳에 활용할 수 있을 것이다.
2020년 05월 24일22분NLP - Bag of words, n-gram
Ai자연어 처리(natural language processing)는 인간의 언어 현상을 기계적으로 분석해서 컴퓨터가 이해할 수 있는 형태로 만드는 자연 언어 이해 혹은 그러한 형태를 다시 인간이 이해할 수 있는 언어로 표현하는 제반 기술을 의미한다. (위키피디아) 간단하게 말하면, 자연어의 의미를 분석하여 컴퓨터가 처리할 수 있도록 하는 일 이라고 생각하면 될 것 같다. 텍스트 기계학습 모델을 만들기 위해서는 데이터를 모델에 맞게 변형시켜 주어야 한다. 알고리즘에서 텍스트를 그대로 받아들일수 없기 때문에 받아들일 수 있는 어떤 숫자값으로 변환을 해주어야 한다. 하지만 텍스트는 일단 언어가 제각기 다르기 떄문에 텍스트 자체를 어떻게 숫자화 할지 부터 시작해야한다. 그럼 어떤 방법들이 있는지 살펴보자. BOW(bag of words) !nlp-1 BOW(Bag of words)는 텍스트 데이터를 표현하는 방법 중 하나로 가장 간단하지만 효과적이라 기계학습에서 널리 쓰이는 방법이다. BOW는 텍스트의 구조와 상관없이 단어들을 담는 가방(Bag)으로 생각하면 된다. 세 문장에서 나타나는 단아들을 모으고 세 문장을 각각 binary vector로 표현하는 것이 BOW이다. 각각의 문장을 binary vector로 표현하면 다음과 같다. | the | game | is | fun | interesting | not | funny | |-----|------|----|-----|-----|------|------| | 1 | 1 | 1 | 1 | 0 | 0 | 0 | >The game is fun : [1, 1, 1, 1, 0, 0, 0] | the | game | is | fun | interesting | not | funny | |-----|------|----|-----|-----|------|------| | 1 | 1 | 1 | 0 | 1 | 0 | 0 | >The game is interesting : [1, 1, 1, 0, 1, 0, 0] | the | game | is | fun | interesting | not | funny | |-----|------|----|-----|-----|------|------| | 1 | 1 | 1 | 0 | 0 | 1 | 1 | >The game is not funny : [1, 1, 1, 0, 0, 1, 1] 만약 이 가방에 들어있지 않는 단어가 포함된 문장이 있어도 BOW는 그 없는 단어는 제외하고 있는 단어만을 가지고 벡터로 만들 것이다. 그럼 이 수치로 표현된 값을 어떻게 활용할까? 머신러닝 모델에 입력값으로 사용할 수도 있디만 단순히 계산만으로 문장간의 유사도(Sentence similarity)를 알 수도 있다. 과 문장의 유사도를 구해보자. | 문장 | 벡터값 | |------|-------| | the game is fun | [, , , 1, 0, 0, 0] | | The game is interesting | [, , , 0, 1, 0, 0] | >유사도 = (1x1) + (1x1) + (1x1) + (1x0) + (0x1) + (0x0) + (0x0) = 3 또한 수치로 표한된 값을 이용해 기계학습 모델에 입력값으로 사용할 수가 있다. 문장에 대한 감성분석을 해주는 모델이 있다면, 벡터화된 값을 모델에 입력값으로 사용할 수 있고 모델은 우리가 원하는 또는 의 결과를 출력해 줄 수 있다. !bow 하지만 BOW는 몇 가지 단점이 있다. Sparsity 실제 사전에는 100만개가 넘는 단어들이 있을 수도 있다. 그렇게 되면 벡터의 차원이 100만개가 넘어가기 때문에 실제 문장하나를 표현할 때 대부분의 값이 0이고 그외의 값들은 상당히 적을 것이다. 결국 학습량이 많아지고 컴퓨터 자원도 상당히 많이 사용하게 된다. (the game is fun [1,1,1,1,0,0,0,0,0,0,0,,,,,,0,0,0,0,0]) 빈번한 단어는 더 많은 힘을 가진다. 많이 출현한 단어는 힘이 세진다. 만약 의미없는 단어들이 많이 사용 되었다면 우리가 원하는 결과를 얻기는 어려울 것이다. Out of vocabulary 오타, 줄임말 등의 단어들이 포함되면 굉장히 난감해진다.^^; 단어의 순서가 무시됨 단어의 출현 횟수만 셀수 있고 단어의 순서는 완전히 무시 된다. 단어의 순서가 무시된다는 것은 다른 의미를 가진 문장이 동일한 결과로 해석될 수 있다는 것이다. 전혀 반대의 의미를 가진 두 문장을 보자. !nlp-1 두 문장은 의미가 전혀 반대이지만 BOW를 이용해 처리한다면, 동일한 결과를 반환하게 될 것이다. 이런 단점을 보완하기 위해 좀더 개선된 n-gram이란 것이 있다. BOW는 하나의 토큰을 사용하지만 n-gram은 n개의 토큰을 사용하여 어느정도 단어의 순서를 반영 결과에 반영해 준다. N-Gram BOW를 조금 더 개선하여 단어 하나만을 보는 것이 아니라 주변의 n개 단어를 뭉쳐서 보는 것이다. 뭉쳐진 n개의 단어들을 gram이라고 한다. 단어 개수에 따라 부르는 명칭이 다른데 2개의 단어를 묶어서 사용하면 , 3개면 이라고 부른다. (1-gram은 uni-gram이라고 한다.) 다음 문장을 bi-gram를 사용하여 처리 한다면, "home run" 과 "run home" >bag of words : [home, run] , [run, home] >bi-gram : [home run], [run home] BOW를 사용한다면 두 문장은 같은 백터의 값을 갖게 되겠지만 bi-gram을 사용하면 2개를 뭉쳐서 사용하므로 어느정도의 순서가 보장되는 효과를 볼수 있게 되어 다른 결과 값을 가지게 될 것이다. 이런 특성을 이용해 n-gram은 다음 단어 예측하거나 어떤 단어를 입력 했을때 오타를 발견하고 다른 단어를 추천해 주는데 활용할 수 있다. 텍스트 전처리 (Preprocessing) !nlp-1 BOW나 n-gram이나 모두 많이 쓰이지만 가장 중요한 것은 단어의 전처리가 확실해야 한다는 것이다. 이 글에서는 설명을 위해 간단한 문장만을 사용하여 크게 신경을 쓸 필요는 없겠지만, 자연어 처리를 하다보면 다양한 케이스의 문장들을 접하게 될 것이며 이런 문장들을 토큰화하고 불필요한 단어들은 제거하고 같은 의미의 단어들은 치환하는 등의 고단한 작업 들을 해야 할 것이다. 하지만 다행인 것은 이런 전처리 작업들을 편하게 할 수 있도록 도와주는 좋은 라이브러리들이 있다. 다음 포스팅에서는 전처리에 대해 자세히 살펴보도록 하겠다...
2019년 08월 07일9분TF - 텍스트 분류
Ai지난번에 TF-IDF 및 Word2Vec을 이용해 텍스트를 분류해 보았다. 이번에는 텐서플로를 이용하여 텍스트를 분류 해보자. 여기에서는 인터넷 영화 데이터베이스(Internet Movie Database)에서 수집한 50,000개의 영화 리뷰 텍스트를 담은 IMDB 데이터셋을 사용하겠다. 25,000개 리뷰는 훈련용으로, 25,000개는 테스트용으로 나뉘어져 있고, 훈련 세트와 테스트 세트의 클래스는 균형이 잡혀 있다. 즉 긍정적인 리뷰와 부정적인 리뷰의 개수가 동일하다. 데이터는 Keras에서 제공하는 datasets를 이용하여 다운로드 하자. Text Classification https://www.tensorflow.org/tutorials/keras/basictextclassification?hl=ko 1.13.1 1.16.2 만약 numpy 버전이 1.16.2 이상의 버전이라면 삭제 후 버전을 지정하여 설치를 해야 한다. (imdb.load_data를 하는 도중 오류 발생) > conda install numpy=1.16.2 데이터 준비 keras dataset에 있는 imdb를 사용한다. train과 test 데이터 셋은 각각 25,000개 이며 데이터는 review 자체로 구성 labels는 0또는 1값으로 긍정 부정을 나타냄 > test데이터를 이용하여 해당 Review가 영화에 대해 긍정 또는 부정적인지를 예측해 본다. 훈련 샘플: 25000, 레이블: 25000 실제 데이터를 살펴보면 문자가 아닌 숫자 값이 리스트로 들어있다. [1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32] 이렇게 되어 있는 이유는 추가적인 dictionary에 각 숫자와 단어가 매칭되어 있기 때문이다. 또한 아래와 같이 각각의 데이터 길이도 다른것을 확인 할 수 있다. (218, 189) 하지만 실제로 ML모델에 넣어줄때는 입력 길이는 모두 같아야 한다. 따라서 입력길이를 모두 동일하게 해주는 작업이 필요하다. 먼저 데이터를 보다 자세히 확인해보기 위해 각 데이터의 숫자를 단어로 치환해 보자. word_index word_index.items() pad, start, unknown, unused 값을 나타내기 위해 각 value에 3을 더하고 비어있게 되는 0~3에 각각을 할당한다. 실제로 필요한 dictionary는 숫자가 key이고, 단어가 value인 dictionary이기 때문에 reversewordindex라는 dictionary를 구성하고 숫자로 이루어진 입력데이터를 단어로 치환해 주며 문장으로 출력하는 decode_review 함수를 만든다. decode example 앞서 추가해주었던 , 등이 추가되어 보여지는 것을 확인할 수 있다. 데이터 전치리 위에서 언급했던 각 데이터의 길이가 상이한 것을 처리한다. keras에서 제공하는 preprocessing 함수를 이용하여 모든 데이터를 최대길이로 늘려주면서 빈공간에는 위에서 dictionary에 추가적으로 넣어주었던 pad값을 이용한다. 신경망에 주입하기 전에 텐서로 변환되어야 하는데, 변환하는 방법에는 몇 가지가 있다. 원-핫 인코딩(one-hot encoding) : 정수 배열을 0과 1로 이루어진 벡터로 변환한다. 예를 들어 배열 [3, 5]을 인덱스 3과 5만 1이고 나머지는 모두 0인 10,000차원 벡터로 변환할 수 있다. 그다음 실수 벡터 데이터를 다룰 수 있는 층-Dense 층-을 신경망의 첫 번째 층으로 사용한다. 이 방법은 numwords * numreviews 크기의 행렬이 필요하기 때문에 메모리를 많이 사용하게 된다는 단점이 있다. 다른 방법으로는, 정수 배열의 길이가 모두 같도록 패딩(padding)을 추가해 maxlength * numreviews 크기의 정수 텐서를 만드는 방법이다. 이런 형태의 텐서를 다룰 수 있는 임베딩(embedding) 층을 신경망의 첫 번째 층으로 사용할 수 있다. 여기서는 두 번째 방식을 사용해보자. 영화 리뷰의 길이가 같아야 하므로 pad_sequences 함수를 사용해 길이를 맞추자. 이 작업을 통해 변경된 것을 확인해 보면, 1. traindata, testdata 길이가 동일 2. 배열내 모든 데이터가 256 3. 데이터 형태는 맨 뒤에 0값, 즉 pad값이 포함되어 있음 (256, 256) [ 1 14 22 16 43 530 973 1622 1385 65 458 4468 66 3941 4 173 36 256 5 25 100 43 838 112 50 670 2 9 35 480 284 5 150 4 172 112 167 2 336 385 39 4 172 4536 1111 17 546 38 13 447 4 192 50 16 6 147 2025 19 14 22 4 1920 4613 469 4 22 71 87 12 16 43 530 38 76 15 13 1247 4 22 17 515 17 12 16 626 18 2 5 62 386 12 8 316 8 106 5 4 2223 5244 16 480 66 3785 33 4 130 12 16 38 619 5 25 124 51 36 135 48 25 1415 33 6 22 12 215 28 77 52 5 14 407 16 82 2 8 4 107 117 5952 15 256 4 2 7 3766 5 723 36 71 43 530 476 26 400 317 46 7 4 2 1029 13 104 88 4 381 15 297 98 32 2071 56 26 141 6 194 7486 18 4 226 22 21 134 476 26 480 5 144 30 5535 18 51 36 28 224 92 25 104 4 226 65 16 38 1334 88 12 16 283 5 16 4472 113 103 32 15 16 5345 19 178 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 모델 구성하기 이제 text classification을 수행할 ML모델을 만들어보자. vocab_size는 영화리뷰에 사용되는 단어의 개수이다. 실제로 위에서 단어와 숫자를 매칭하는 dictionary의 사이즈 보다 크지만, 해당 데이터에서는 10000개의 단어 이내로 리뷰가 작성되었다. 각 레이어에 대한 설명은 다음과 같다. 1. embedding : 숫자로 인코딩 되어있는 각 단어를 사용하며 각 단어 인덱스에 대한 벡터를 찾는다. 이러한 데이터는 추후 모델이 핫습하는데 사용된다. 2. GlobalAveragePooling1D : 각 예시에 대해 sequence 차원을 평균하여 고정된 길이의 벡터를 출력한다. 3. Dense : 첫번째 Dense 레이어를 통해서 고정길이로 출력된 vector값을 통해 16개의 hidden unit을 가진 fully-connected layer를 통과시킨다. 이후 두번째 Dense 레이어는 단일 출력 노드를 가지고 시그모이드 활성화 함수를 사용함으로써 결과에 대해 0~1 사이의 값을 가지도록 한다. _ Layer (type) Output Shape Param # ================================================================= embedding_2 (Embedding) (None, None, 16) 160000 _ globalaveragepooling1d_2 ( (None, 16) 0 _ dense_4 (Dense) (None, 16) 272 _ dense_5 (Dense) (None, 1) 17 ================================================================= Total params: 160,289 Trainable params: 160,289 Non-trainable params: 0 _ 모델 구성에 마지막으로 loss function과 optimizer를 설정한다. 모델 훈련하기 모델을 훈련하기에 앞서 10000개의 데이터를 분리하여 validation set을 만들자. 모델이 새롭게 접하는 데이터에 대한 accuracy와 loss등을 확인하기 위함이다. Train on 15000 samples, validate on 10000 samples Epoch 1/40 15000/15000 [==============================] - 1s 99us/sample - loss: 0.6925 - acc: 0.5343 - valloss: 0.6918 - valacc: 0.5347 Epoch 2/40 15000/15000 [==============================] - 1s 69us/sample - loss: 0.6899 - acc: 0.6281 - valloss: 0.6880 - valacc: 0.6741 Epoch 3/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.6835 - acc: 0.7101 - valloss: 0.6792 - valacc: 0.7429 Epoch 4/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.6699 - acc: 0.7408 - valloss: 0.6623 - valacc: 0.7469 Epoch 5/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.6463 - acc: 0.7775 - valloss: 0.6360 - valacc: 0.7647 Epoch 6/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.6127 - acc: 0.7950 - valloss: 0.6017 - valacc: 0.7821 Epoch 7/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.5707 - acc: 0.8089 - valloss: 0.5601 - valacc: 0.7975 Epoch 8/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.5240 - acc: 0.8279 - valloss: 0.5180 - valacc: 0.8142 Epoch 9/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.4775 - acc: 0.8426 - valloss: 0.4773 - valacc: 0.8298 Epoch 10/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.4343 - acc: 0.8581 - valloss: 0.4416 - valacc: 0.8392 Epoch 11/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.3966 - acc: 0.8707 - valloss: 0.4114 - valacc: 0.8505 Epoch 12/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.3645 - acc: 0.8798 - valloss: 0.3880 - valacc: 0.8537 Epoch 13/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.3385 - acc: 0.8867 - valloss: 0.3670 - valacc: 0.8615 Epoch 14/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.3154 - acc: 0.8932 - valloss: 0.3519 - valacc: 0.8651 Epoch 15/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.2967 - acc: 0.8977 - valloss: 0.3393 - valacc: 0.8694 Epoch 16/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.2800 - acc: 0.9027 - valloss: 0.3290 - valacc: 0.8711 Epoch 17/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2651 - acc: 0.9080 - valloss: 0.3207 - valacc: 0.8753 Epoch 18/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2519 - acc: 0.9119 - valloss: 0.3132 - valacc: 0.8780 Epoch 19/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2401 - acc: 0.9159 - valloss: 0.3070 - valacc: 0.8791 Epoch 20/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.2295 - acc: 0.9203 - valloss: 0.3029 - valacc: 0.8798 Epoch 21/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2190 - acc: 0.9240 - valloss: 0.2990 - valacc: 0.8792 Epoch 22/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2101 - acc: 0.9261 - valloss: 0.2951 - valacc: 0.8828 Epoch 23/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2011 - acc: 0.9291 - valloss: 0.2934 - valacc: 0.8819 Epoch 24/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.1933 - acc: 0.9325 - valloss: 0.2905 - valacc: 0.8837 Epoch 25/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1853 - acc: 0.9369 - valloss: 0.2883 - valacc: 0.8846 Epoch 26/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1783 - acc: 0.9395 - valloss: 0.2880 - valacc: 0.8839 Epoch 27/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1715 - acc: 0.9435 - valloss: 0.2868 - valacc: 0.8846 Epoch 28/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1650 - acc: 0.9460 - valloss: 0.2861 - valacc: 0.8845 Epoch 29/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1596 - acc: 0.9487 - valloss: 0.2868 - valacc: 0.8839 Epoch 30/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1537 - acc: 0.9511 - valloss: 0.2859 - valacc: 0.8855 Epoch 31/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1477 - acc: 0.9538 - valloss: 0.2861 - valacc: 0.8857 Epoch 32/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1423 - acc: 0.9558 - valloss: 0.2867 - valacc: 0.8857 Epoch 33/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1370 - acc: 0.9571 - valloss: 0.2882 - valacc: 0.8858 Epoch 34/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1324 - acc: 0.9605 - valloss: 0.2892 - valacc: 0.8868 Epoch 35/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1282 - acc: 0.9607 - valloss: 0.2907 - valacc: 0.8871 Epoch 36/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1234 - acc: 0.9636 - valloss: 0.2923 - valacc: 0.8866 Epoch 37/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1189 - acc: 0.9657 - valloss: 0.2941 - valacc: 0.8860 Epoch 38/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1148 - acc: 0.9671 - valloss: 0.2968 - valacc: 0.8852 Epoch 39/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1113 - acc: 0.9683 - valloss: 0.2990 - valacc: 0.8846 Epoch 40/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1073 - acc: 0.9699 - valloss: 0.3009 - valacc: 0.8841 Epoch 1에서 40까지 진행이 되고 해당 Epoch에서의 acc 및 loss를 확인할 수 있다. 이제 모델 평가 결과를 확인해 보자. 25000/25000 [==============================] - 1s 21us/sample - loss: 0.3204 - acc: 0.8736 [0.3203906687307358, 0.8736] 실제로 더 진보된 모델이라고 하기 위해서는 약 95% 이상의 정확도를 필요로 한다. 하지만 매우 단순한 방식을 사용했기 때문에 약 87% 정도의 정확도를 달성한 것을 확인 할 수 있다. 결과를 정확도와 오차의 그래프로 확인해 보자. 정확도와 손실 그래프 그리기 은 History 객체를 반환한다. 여기에는 훈련하는 동안 일어난 모든 정보가 dictionary로 담겨 있다. dictkeys(['loss', 'acc', 'valloss', 'val_acc']) 키를 확인해 보면 네개의 항목이 있다. 이는 훈련과 검증 단계에서 모니터링하는 지표들이다. 훈련 손실과 검증 손실을 그래프로 그려 보고, 훈련 정확도와 검증 정확도도 그래프로 그려 비교해 보자. !png !png 점선은 훈련 loss와 accuracy이고 실선은 검증 loss와 accuracy를 나타낸다. 훈련(점선)에서 loss는 epoch가 진행되면서 감소하고 accuracy는 증가한다. 하지만 검증(실선)에서는 약 20번째 epoch 이후가 최적점인 것 같다. 이는 훈련 데이터에서만 잘 동작하는 과대적합 때문일 것이다. 이 지점 부터는 모델이 과도하게 최적화되어 테스트 데이터에서 일반화되기 어려운 훈련 데이터 특정 표현을 학습하게 된다. 여기에서는 과대적합을 막기 위해 단순히 20번째 epoch 근처에서 훈련을 멈출 수 있다.
2019년 08월 07일20분🤩 1. Angular 8
AngularAngular 8버전이 출시 되었다. 이번 버젼은 최신 브라우저에서 어플리케이션의 구동 시간을 단축시키고, CLI와 함께 사용할 수 있는 새로운 API를 제공한다. 필자는 기존 Angular 6에서 8버전으로 업그레이드를 하였는데, 성능적인 면에서 많이 향상이 된 것을 체감적으로도 느낄 수 있었다. Update update.angular.io를 방문해 자세한 정보와 가이드를 참고 하자. Differential loading 차등 로딩(Differential loading)은 브라우저가 자신의 능력에 따라 최신 자바스크립트와 원시 자바스크립트를 선택하는 과정을 말한다. 기본값으로 Angular 8은 최신 빌드(es2015)와 원시 빌드(es5)를 각각 수행하고 사용자가 어플리케이션을 로드했을 때, 사용자의 브라우저는 자동으로 필요한 번들을 선택할 것이다. ng update 명령어를 쓰면 Angular는 차등 로딩을 위해 프로젝트의 tsconfig.json를 업데이트 한다. Angular CLI는 차등 로딩의 수행 여부를 결정하기 위해 tsconfig.json 파일의 target 프로퍼티에 명시된 자바스크립트의 버젼을 확인한다. target 프로퍼티가 es2015로 설정되었을 때, Angular는 두 개의 번들을 생성하고 각각 이름을 부여하고 브라우저는 런타임시에 script 태그의 속성을 통해 어떤 번들을 로드할지 결정한다. 최신 브라우저에서 초기 번들 사이즈를 40kB 이상 절감 했고, 커뮤니티에서도 얼마나 최신 자바스크립트를 많이 사용했는가에 따라 7-20% 정도의 절감 효과를 보았다는 이야기가 나왔다고 한다. !01 자세한 내용은 여기를 참고하자. Route Configurations use Dynamic Imports Angular는 어플리케이션의 일부를 라우터를 이용한 지연 로딩 기법으로 불러올 것을 권장한다. 이는 라우트 설정의 loadChildren 프로퍼티를 통해 설정할 수 있는데, 8 버전에서는 동적 임포트의 산업 표준으로 변경되었다. 이는 VSCode 같은 툴에서 해당 임포트 구문을 이해하고 교정해줄 수 있는 에디터의 지원성을 높일 것이다. ng update 명령어를 쓰면 Angular는 자동으로 해당 구문을 변경한다. Builder APIs in the CLI Angular Schematics가 ng new, ng generate, ng add, ng update와 같은 명령어에 접근하듯, 새로운 Builder API는 ng build와 ng test 그리고 ng run에 접근해 빌드나 배포 과정을 후킹할 수 있도록 한다. 공식 API 문서를 참고. CLI의 Workspace API 기존에 Schematics를 사용하려면 angular.json을 열어 수동으로 작업 저장소 설정을 수정해야 했다. Angular 8은 angular.json을 더 쉽게 관리할 수 있는 API를 제공한다. Workspace API에 대한 자세한 설명은 여기를 참고 Web Worker 지원 Web Workers는 cpu 자원을 많이 사용하는 어플리케이션에서 어플리케이션의 속도를 향상시킬 수 있는 좋은 방안이다. Web Workers는 이미지나 비디오 처리를 백그라운드 스레드로 옮길 수 있게 해준다. angular.io 공식홈도 내부의 검색과 색인을 위해 Web Workers를 사용했다고 한다. 이제 CLI로 Web Workers를 생성할 수 있고, worker를 생성하려면 다음 명령어를 사용하면 된다. web worker를 생성했다면, 원래 사용하던 대로 사용하면 된다. CLI가 web worker를 알아서 번들화 하고 정리해준다. 더 자세한 Angular CLI의 Web Workers에 대해서 알려면 여기를 참고. Deprecation Angular는 공개 API가 n + 2 버젼을 지원하도록 한다. 즉 8.1 버젼에서 폐기 처분될 기능은 9 버젼과 10 버젼까지는 작동할 것이라는 이야기이다. 예를 들어, 버젼 8에서 platform-webworker를 폐기 하게 된다. Angular에서 폐기, 제거된 기능 관련하여 정리된 리스트를 보려면 Deprecation Guide를 참고하자. Ivy & Bazel Ivy를 8.0 버전에서 프리뷰 옵션 형태로 사용이 가능하다. Angular Ivy는 가상 DOM의 한계를 지닌 이전 렌더러의 문제를 해결하기 위해 만들어 졌다. Ivy는 원본보기 엔진의 크기가 작고, 빠르고 간단하며, Tree Shaking의 장점과 Incremental DOM의 메모리 사용 공간을 줄인다. Ivy를 통해 렌더링하는 동안 메모리 할당을 최소화하고 증분 빌드를 허용하는 한 번에 하나의 파일을 컴파일하여 메모리가 제한된 장치에서 앱 성능을 향상 시키도록 설계되었다. 즉, Ivy는 컴파일 프로세스를 단순화하여 압축한다. 그리고 더 작고 더 많은 원자 함수로 나누기 때문에 Ivy는 이전 렌더러에 비해 Tree Shaking에 더 최적화되어 있다. Ivy enable 을 편집하여 Ivy를 선택하고 angularComplierOption 섹션을 추가하고 enableIvy를 true로 설정한다. 새로운 Angular CLI 프로젝트의 경우, newscript를 실행할 때 --enableIvy 플래그를 사용할 수 있다. 그 결과, 번들 크기의 15%로 줄어들었다면 이는 웹 사이트 로딩 시간이 15% 더 빨라짐을 의미한다. 참고 (blog.angularindepth.com - Angular Ivy)
2019년 07월 22일8분NLP - 텍스트 분류 (Word2Vec) -
Ai이번에는 word2vec을 활용하여 모델을 구현 해보자. word2vec을 활용한 모델 구현 word2vec을 활용해 모델을 만들기 위해서는 먼저 각 단어에 대해 word2vec으로 백터화해야 한다. word2vec의 경우 단어로 표현된 리스트를 입력값으로 넣어야 한다. word2vec 백터화 이제 word2vec 모델 학습을 진행하기 앞서 word2vec 모델의 하이퍼 파라미터를 설정해야 한다. num_fratures : 각 단어에 대한 임베딩된 벡터의 차원을 정한다. minwordcount : 모델에 의미 있는 단어를 가지고 학습하기 위해 적은 빈도 수의 단어들은 학습하지 않는다. num_workers : 모델에 의미 있는 단어를 가지고 학습하기 위해 적은 빈도 수의 단어들은 학습하지 않는다. context : word2vec을 수행하기 위한 컨텍스트 윈도우 크기를 지정한다. downsampling : word2vec 학습을 수행할 때 더 빠른 학습을 위해 정답 단어 라벨에 대한 다운샘플링 비율을 지정한다. (보통 0.001이 좋은 성능을 낸다고 한다) 로깅을 할 떄 format을 위와 같이 지정하고, 로그 수준을 INFO로 하면 word2vec의 학습과정에서 로그 메시지를 양식에 맞게 INFO 수준으로 볼 수 있다. 2019-07-12 21:56:43,872 : INFO : collecting all words and their counts 2019-07-12 21:56:43,872 : INFO : PROGRESS: at sentence #0, processed 0 words, keeping 0 word types Training model... 2019-07-12 21:56:44,092 : INFO : PROGRESS: at sentence #10000, processed 1205223 words, keeping 51374 word types 2019-07-12 21:56:44,305 : INFO : PROGRESS: at sentence #20000, processed 2396605 words, keeping 67660 word types 2019-07-12 21:56:44,406 : INFO : collected 74065 word types from a corpus of 2988089 raw words and 25000 sentences 2019-07-12 21:56:44,406 : INFO : Loading a fresh vocabulary 2019-07-12 21:56:44,443 : INFO : min_count=40 retains 8160 unique words (11% of original 74065, drops 65905) 2019-07-12 21:56:44,443 : INFO : min_count=40 leaves 2627273 word corpus (87% of original 2988089, drops 360816) 2019-07-12 21:56:44,454 : INFO : deleting the raw counts dictionary of 74065 items 2019-07-12 21:56:44,469 : INFO : sample=0.001 downsamples 30 most-common words 2019-07-12 21:56:44,469 : INFO : downsampling leaves estimated 2494384 word corpus (94.9% of prior 2627273) 2019-07-12 21:56:44,481 : INFO : estimated required memory for 8160 words and 300 dimensions: 23664000 bytes 2019-07-12 21:56:44,481 : INFO : resetting layer weights 2019-07-12 21:56:44,577 : INFO : training model with 4 workers on 8160 vocabulary and 300 features, using sg=0 hs=0 sample=0.001 negative=5 window=10 2019-07-12 21:56:45,578 : INFO : EPOCH 1 - PROGRESS: at 53.88% examples, 1354613 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:46,589 : INFO : EPOCH 1 - PROGRESS: at 97.12% examples, 1208841 words/s, inqsize 8, outqsize 0 2019-07-12 21:56:46,625 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:46,631 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:46,639 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:46,645 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:46,649 : INFO : EPOCH - 1 : training on 2988089 raw words (2494785 effective words) took 2.1s, 1208654 effective words/s 2019-07-12 21:56:47,643 : INFO : EPOCH 2 - PROGRESS: at 51.68% examples, 1296637 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:48,565 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:48,565 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:48,579 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:48,587 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:48,587 : INFO : EPOCH - 2 : training on 2988089 raw words (2494872 effective words) took 1.9s, 1287927 effective words/s 2019-07-12 21:56:49,584 : INFO : EPOCH 3 - PROGRESS: at 52.03% examples, 1307272 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:50,495 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:50,504 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:50,504 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:50,513 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:50,513 : INFO : EPOCH - 3 : training on 2988089 raw words (2494507 effective words) took 1.9s, 1297134 effective words/s 2019-07-12 21:56:51,508 : INFO : EPOCH 4 - PROGRESS: at 52.60% examples, 1319590 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:52,421 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:52,438 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:52,438 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:52,444 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:52,444 : INFO : EPOCH - 4 : training on 2988089 raw words (2494292 effective words) took 1.9s, 1293999 effective words/s 2019-07-12 21:56:53,445 : INFO : EPOCH 5 - PROGRESS: at 54.20% examples, 1360524 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:54,212 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:54,228 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:54,235 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:54,241 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:54,242 : INFO : EPOCH - 5 : training on 2988089 raw words (2494012 effective words) took 1.8s, 1390220 effective words/s 2019-07-12 21:56:54,242 : INFO : training on a 14940445 raw words (12472468 effective words) took 9.7s, 1291189 effective words/s word2vec으로 학습시킨 모델의 경우 모델을 따로 저장해두면 이후 다시 사용할수 있기 때문에 저장해두자 2019-07-12 22:00:40,807 : INFO : saving Word2Vec object under 300features40minwords10context, separately None 2019-07-12 22:00:40,807 : INFO : not storing attribute vectors_norm 2019-07-12 22:00:40,807 : INFO : not storing attribute cum_table C:\Users\nicey\.conda\envs\nlp\lib\site-packages\smartopen\smartopenlib.py:398: UserWarning: This function is deprecated, use smartopen.open instead. See the migration notes for details: https://github.com/RaRe-Technologies/smart_open/blob/master/README.rst#migrating-to-the-new-open-function 'See the migration notes for details: %s' % MIGRATIONNOTES_URL 2019-07-12 22:00:41,229 : INFO : saved 300features40minwords10context 에제 만들어진 word2vec 모델을 활용해 선형 회귀 모델을 학습해보자. 우선 학습을 위해 하나의 리뷰를 같은 형태의 입력값으로 만들어야 한다. 지금은 word2vec 모델에서 각 단어가 벡터로 표현돼 있다. 그리고 리뷰마다 단어의 개수가 모두 다르기 때문에 입력값을 하나으 형태로 만들어야 한다. 가장 단순한 방법은 문장에 있는 모든 단어의 벡터값에 대해 평균을 내서 리뷰 하나당 하나의 벡터로 만드는 방법이 있다. 그럼 하나의 리뷰에 대해 전체 단어의 평균값을 계산하는 함수를 구현하자 words : 단어의 모음인 하나의 리뷰가 들어간다. model : word2vec 모델을 넣는 공이며, 우리가 학습한 word2vec 모델이 들어간다. num_features : word2vec으로 임베딩할 때 정했던 벡터의 차원 수를 뜻한다. 하나의 벡터를 만드는 과정을 빠르게 하기 위해 np.zeros를 사용해 미리 모두 0값을 가지는 벡터를 만든다. 그리고 문장의 단어가 모델 단어사전에 속하는지 보기 위해 model.wv.index2word를 set객체로 생성해서 index2word_set 변수에 할당한다. 다음 반복문을 통해 리뷰를 구성하는 단어에 대해 임베딩된 벡터가 있는 단어 벡터의 합을 구하고 사용한 단어의 전체 개수로 나누어 평균 벡터의 값을 구한다. 문장에 특징값을 만들 수 있는 함수를 구현했다면 이제 앞에서 정의한 함수를 사용해 전체 리뷰에 대해 각 리뷰의 평균 벡터를 구하는 함수를 정의하자 reviews : 학습 데이터인 전체 리뷰 데이터를 입력 model : word2vec 모델을 입력 num_features : word2vec으로 임베딩할 때 정했던 벡터의 차원 수 전체 리뷰에 대한 평균 벡터를 담을 0으로 채워진 numpy 배열을 미리 만든다. 배열은 2차원, 배열의 행에는 각 문장에 대한 길이, 열에는 평균 벡터의 차원수 즉 크기를 입력. 그리고 각 리뷰에 대해 반복문을 돌면서 각 리뷰에 대해 특징 값을 만든다. 구현한 함수를 사용해 실제 학습에 사용될 입력값을 만들어 보자. C:\Users\nicey\.conda\envs\nlp\lib\site-packages\ipykernel_launcher.py:13: DeprecationWarning: Call to deprecated (Method will be removed in 4.0.0, use self.wv.getitem() instead). del sys.path[0] 학습과 검증 데이터셋 분리 모델 선언 및 학습 C:\Users\nicey\.conda\envs\nlp\lib\site-packages\sklearn\linear_model\logistic.py:432: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning. FutureWarning) LogisticRegression(C=1.0, class_weight='balanced', dual=False, fitintercept=True, interceptscaling=1, l1_ratio=None, maxiter=100, multiclass='warn', n_jobs=None, penalty='l2', random_state=None, solver='warn', tol=0.0001, verbose=0, warm_start=False) class_weight을 로 설정했다. 이는 각 라벨에 대해 균형있게 학습하기 위함이다. 검증 데이터셋을 이용한 성능 평가 Accuracy: 0.874200 Precision: 0.869141 Recall: 0.883287 F1-Score: 0.876157 AUC: 0.940927 학습 결과를 확인해 보면 TF-IDF를 사용해서 학습한 것보다 상대적으로 성능이 조금 떨어지는 것을 볼 수 있다. word2vec이 단어 간의 유사도를 보는 관점에서는 분명히 효과적일 수는 있지만 항상 좋은 성능을 보장하지는 않는다는 점을 알 수 있다. 데이터 제출 | | review | id | |-|-|-| | 0 | naturally film main themes mortality nostalgia... | "12311_10" | | 1 | movie disaster within disaster film full great... | "8348_2" | | 2 | movie kids saw tonight child loved one point k... | "5828_4" | | 3 | afraid dark left impression several different ... | "7186_2" | | 4 | accurate depiction small time mob life filmed ... | "12128_7" | C:\Users\nicey\.conda\envs\nlp\lib\site-packages\ipykernel_launcher.py:13: DeprecationWarning: Call to deprecated (Method will be removed in 4.0.0, use self.wv.getitem() instead). del sys.path[0]
2019년 07월 19일14분NLP - 텍스트 분류 (TF-IDF) -
Ai이전 글에서는 데이터를 모델에 적용하기 전에 데이터에 대해 이해하고 정제하는 과정인 데이터 전처리 과정을 진행했다. 이제 전처리된 데이터를 가지고 TF-IDF를 활용한 모델을 구현할 것이다. > 선형 회귀 모델 : 종속변수와 독립변수 간의 상관관계를 모델링하는 방법 > 로지스틱 회귀 모델 : 선형 모델의 결과값에 로지스틱 함수를 적용하여 0 ~ 1 사이의 값을 갖게 하여 확률로 표현 > TF-IDF : TF(Term Frequency, 단어의 빈도), IDF(역문서 빈도, Inverse Document Frequency) 쉽게 말하자면 문장에서 단어의 빈도수를 계산하되 너무 자주 등장하는 단어는 크게 의미를 두지 않도록 가중치를 낮게 주자는 의미. TF-IDF를 활용해 문장 벡터를 만들기 위한 TfidfVectorizer를 사용하기 위해서는 입력값이 텍스트로 이루어진 데이터 형태여야 하기 때문에 전처리한 결과 중 numpy배열이 아닌 정제된 텍스트 데이터를 사용해야 한다. >훈련 데이터 : train_clean.csv >테스트 데이터 : test_clean.csv | | review | sentiment | |-|-|-| | 0 | stuff going moment mj started listening music ... | 1 | | 1 | classic war worlds timothy hines entertaining ... | 1 | | 2 | film starts manager nicholas bell giving welco... | 0 | | 3 | must assumed praised film greatest filmed oper... | 0 | | 4 | superbly trashy wondrously unpretentious explo... | 1 | min_df : 설정한 값보다 특정 토큰의 df값이 더 적게 나오면 벡터화 과정에서 제거 analyzer : 분석하기 위한 기준 단위(word:단어 하나를 단위로, char:문자 하나를 단위로) sublinear_tf : 문서의 단어 빈도 수에 대한 스무딩(smoothing) 여부 ngram_range : 빈도의 기본 단위를 설정할 n-gram 범위 max_features : 각 벡터의 최대 길이 학습과 검증 데이터셋 분리 해당 입력값을 모델에 적용하기전 학습데이터의 일부를 검증 데이터로 따로 분리하자. 입력값인 X와 정답 라벨을 numpy 배열로 만든 y에 대해 적용해서 학습데이터와 검증데이터로 나누었다. 모델 선언 및 학습 선형 회귀 모델을 만들기 위해 LogisticRegression을 사용하고, class_weight를 'balanced'로 설정해서 각 라벨에 대해 균형 있게 학습할 수 있게 하자. C:\Users\nicey\.conda\envs\nlp\lib\site-packages\sklearn\linear_model\logistic.py:432: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning. FutureWarning) LogisticRegression(C=1.0, class_weight='balanced', dual=False, fitintercept=True, interceptscaling=1, l1_ratio=None, maxiter=100, multiclass='warn', n_jobs=None, penalty='l2', random_state=None, solver='warn', tol=0.0001, verbose=0, warm_start=False) 검증 데이터로 성능 평가 Accuracy : 0.859600 성능 평가 방법으로 정밀도(precision), 재현율(recall), f1-score, auc 등의 다양한 지표가 있지만 여기서는 정확도(Accuracy)만 측정하였다. 평가 결과 약 86%의 정확도를 보였다. 성능이 생각보다 나오지 않을 때는 하이퍼파라미터를 수정하거나 다른 기법들을 추가해서 성능을 올려보자. 검증 데이터의 성능이 만족할 만큼 나온다면 평가 데이터를 적용하면 된다. 데이터 제출하기 생성한 모델을 활용해 평가 데이터 결과를 예측하고 캐글에 제출할 수 있도록 파일로 저장하자. 우선 전처리한 텍스트 형태의 평가 데이터를 불러오자. | | review | id | |-|-|-| | 0 | naturally film main themes mortality nostalgia... | "12311_10" | | 1 | movie disaster within disaster film full great... | "8348_2" | | 2 | movie kids saw tonight child loved one point k... | "5828_4" | | 3 | afraid dark left impression several different ... | "7186_2" | | 4 | accurate depiction small time mob life filmed ... | "12128_7" | 해당 데이터를 대상으로 이전에 학습 데이터에 대해 사용했던 객체를 사용해 TF-IDF 값으로 벡터화한다. 백터화할 때 평가 데이터에 대해서는 fit을 호출하지 않고 그대로 transform만 호툴한다. 이미 학습 데이터에 맞게 설정했고, 그 설정에 맞게 평가 데이터도 변환을 하면 된다. 이제 이 값으로 예측한 후 예측값을 하나의 변수로 할당하고 출력해보자. [1 0 1 ... 0 1 0] 결과를 보면 각 데이터에 대해 긍정, 부정 값을 가지고 있다. 이제 이 값을 캐글에 제출하기 위해 csv 파일로 저장하자. 캐글에 제출하기 위한 데이터 형식은 각 데이터의 고유한 id 값과 결과값으로 구성되어야 한다.
2019년 07월 02일7분딥러닝 - 딥러닝의 이해
Ai개념 딥러닝이라는 용어는 2006년 캐나다 토론토 대학교의 제프리 힌튼(Geoffrey Hinton) 교수의 논문을 통해 처음 사용이 되었다. 하지만 딥러닝은 사실 새로운 개념이 아니다. 오래전부터 있어오던 인공신경망(Artificial Neural Network, ANN)과 크게 다를 바 없다. '인공신경망'이라고 하면 복잡한 뇌 구조를 생각하면서 어렵게 생각이 들겠지만 실제 뉴런의 행동을 살펴보면 어떻게 이렇게 단순한 행동으로 이루어질까 싶을 정도로 심플하다. !deep-1 입력신호가 오면 길이에 따른 가중치(weight)를 부여하고 임계값(bias)을 더한 후 축색돌기를 통해 이동한다. 그리고 그 값이 조건에 만족한다면 다음 뉴런으로 넘어가고 그렇지 않다면 넘어가지 않게 된다. 이러한 실제 뉴런에서 이뤄지는 과정을 그대로 수학적으로 구현한 것이다. 컴퓨터가 사진 속에서 고양이를 검출해내야 한다고 생각해보자. '고양이'라는 추상적 이미지는 아마 선, 면, 형상, 색깔, 크기 등 다양한 요소들이 조합된 결과물일 것이다. 이것은 아마 '선 30cm 이상은 고양이, 이하는 고양이 아님', 또는 '갈색은 고양이, 빨간색은 고양이 아님' 처럼 간단한 선형 구분으로는 식별해 낼 수 없는 문제이다. 딥러닝은 이 과제를 선 긋고 왜곡하고 합하고를 반복하며 복잡한 공간 속에서의 최적의 구분선을 만들어 내는 목적을 가지고 있다. !deep-1 파란선과 빨간선의 영역을 구분한다고 생각해보자. 그냥 구분 선을 긋는다면 아마 왼쪽처럼 불완전하게 그을 수 있을 것이다. 하지만 공간을 왜곡하면 오른쪽 같이 아름답게 구분선을 그릴 수 있다. 이처럼 인공신경망은 선 긋고, 구기고, 합하고를 반복하여 데이터를 처리한다. 그럼 어떠한 규칙으로 선을 긋고 공간을 왜곡할까? 바로 데이터에 근거하는 것이다. 일단 대충 선을 긋고 그것들을 살살 움직여가며 구분 결과가 더 좋게 나오도록 선을 움직이는 것인데 이러한 과정을 최적화(optimization)이라고 한다. 딥러닝은 아주 많은 데이터와 아주 오랜 시간의 최적화를 통해 데이터를 학습하게 된다. (양에는 장사 없다고나 할까...) !deep-1 딥러닝은 하나의 뉴런이 아닌 수천 수억개의 뉴런들이 연결되어 이뤄진다. 즉 인공 신경 또한 수많은 연결들을 통해 망을 이룬다. 크게 입력층(input layer)과 출력층(output layer) 그리고 그 사이에 있는 layer들을 묶어서 은닉층(hidden layer)이라고 한다. 위 그림에서는 모든 노드가 연결되어있지만, 꼭 다 연결되어있어야 하는 것은 아니다. 단지 내가 어떻게 신경망을 구성하냐에 따라 정해지는 것이다. 저렇게 망이 이뤄져 있는 것은 알겠는데, 대체 어떻게 학습을 한다는건지 이해가 안 될 수 있다. 내가 고양이 사진을 입력에 넣었다면 출력에는 고양이라는 결과가 나와야 맞는 것이다. 고양이가 맞다면 그냥 넘어가면 되지만, 고양이가 아니라고 결과가 나왔다면 이것은 문제가 있는 것이다. 따라서 다시 뒤로 돌아가면서 각 인공 신경의 가중치(weight)와 임계값(bias) 값을 수정하게 된다. 이러한 과정을 반복하며 weight와 bias 값을 조정하고 고양이라는 결과가 나오도록 '학습' 하는 것이다. !deep-1 2012년 이미지넷에서 이미지 분류 대회가 있었다. 1000개의 카테고리와 100만개의 이미지로 구성되어 분류 정확도를 겨루는 대회이고, 아래 사진만 보더라도 딥러닝이 이미지 인식 부문에서 얼마나 큰 영향을 끼치고 있는지 알 수 있다. 이 대회 이전에는 기계의 이미지 인식률이 75%를 넘지 못했고 80% 이상의 인식률은 불가능이라는 인식이 있었다. 하지만 이 대회에서 힌튼 교수의 제자 알렉스가 알렉스넷(AlexNet)이라는 딥러닝 기반 알고리즘으로 84.7%를 찍었고, 이후로는 대부분의 참가자들이 딥러닝으로 방향을 돌렸다. 현재는 오류율이 5% 이하의 정확도로 인간의 인식수준을 뛰어넘었다. 딥러닝이 대단한 것은 일반적인 기계 학습과 달리 특징 추출(feature extraction)이 자동적으로 이루어지는 점 이다. 기존에는 효과적인 특징을 추출하기 위해 관련 분야 전문가가 오랜 시간동안 직접 특징을 추출하는 수식이나 방법을 고안해야 했다. 이 방법은 개발, 평가 및 보완에 많은 시간이 걸리는데 딥러닝은 이런 과정을 컴퓨터가 대신 하도록 알고리즘을 짠 것으로, 사람에 비해 훨씬 빠르고 효과적으로 수행해도록 학습시켜준다. 발전과정 딥러닝의 발전과정을 보면 아주 오래전부터 시작이 되었고 지금에 이르기까지 많은 이들의 노력이 있었다. 퍼셉트론은 인간의 두뇌 움직임을 수학적으로 구성하였다. !deep-1 !deep-1 그 당시 AND와 OR의 경우 퍼셉트론을 통한 선형 분리(linearly separable)가 가능했다. 따라서 AND / OR 연산이 가능하게 훈련한 퍼셉트론을 보고 많은 사람들은 기계의 학습 가능성에 대해 큰 기대를 하게 되었다. 하지만 이러한 그들의 엄청난 기대에 찬물을 끼얹어버린 것이 XOR 연산에 대한 불가능이었다. 당시 하나의 인공 신경. 즉 퍼셉트론으로는 선형 분리가 불가능해 XOR 연산에 대한 학습이 불가능했다. 1969년 이 문제를 해결하기 위해 퍼셉트론을 다중으로 겹치면 이 문제를 해결할 수 있음을 증명한 다중 계층 퍼셉트론이 등장하지만 레이어가 복잡해질수록 연산이 복잡해져서 현실적으로 파라미터값을 구하는 것이 불가능 하였다. 데이터가 모델을 스스로 찾아내는 역전파(Backpropagation) 등장 즉 앞의 진행방향에서 고쳐가는 것이 아니라 결과를 보고 뒤로 가면서 weight와 bias를 조정하는 방법을 고안 하였고, XOR 뿐 아니라 좀 더 복잡한 과정도 해결할 수 있음을 보이며 다시 인공 신경망은 사람들의 관심을 끌기 시작했다. !deep-1 (터미네이터2 심판의 날 (1991) 대사 中) 영화 중반부에 터미네이터인 아놀드의 대사 중에 "My CPU is a neural-net processor." 라는 말이 나온다. 스스로 학습할 수 있는 기계를 neural-net processor 라는 말로 설명했고 이 당시에도 neural network에 관심이 있었음을 알 수 있다. 신경망이 깊어질수록 원하는 결과를 얻을 수 없다. 오히려 성능이 저하되는 경우가 발생. 다음의 문제를 해결하는데 10년이 걸리게 된다. 1. Overfitting (과하거나) 2. Vanishing Gradient (덜하거나) 3. Too slow (느리거나) 이런 문제를 해결하기 위해 연구하는 10년 동안 GUP를 활용한 연산시도, 알고리즘의 발전이 있게 된다.(SVM, Random Forest 등장) 1. 과한 적합 해결 Droupout 2. 덜한 적합 해결 ReLU(Rectified Linear Unit) : 수렴속도가 시그모이드류 함수 대비 6배 빠름 3. 느린 적합 해결 Adam : 확룰적 경사 하강법(SGD)에서 더 나아가 학습속도와 운동량을 고려한 옵티마이저가 등장 이러한 여러번의 실패로 인해 이후 나온 논문에서는 neural net이라는 단어를 찾을 수 없었고 neural 대신 deep 이라는 단어를 사용했다. 이 당시 neural network 라는 말만 들어가면 논문에서 거절당한다는 말이 있을 정도로 신경망이 외면받던 시기였기 때문에 좀 더 사람들의 이목을 끌 수 있는 단어를 택했던 것 같다. 마치며 딥 러닝이 화두가 되고 있는 것은 비교적 최근의 일이지만, 딥 러닝의 기본 구조인 인공 신경망(Artificial Neural Network)의 역사는 오래되었다. 1957년에 등장한 초기 신경망인 퍼셉트론(Perceptron)으로 시작해 최근에 쓰이고 있는 발전된 신경망 RNN, LSTM에 대해서도 좀더 자세히 알아보면 좋을 것 같다.
2019년 05월 16일13분NLP - 자연어처리 - KoNLPy (코엔엘파이)
Ai자연어 처리에서 각 언어마다 모두 특징이 다르기 때문에 동일한 방법을 사용하기는 어려울 것이다. 한글에도 NLTK나 Spacy 같은 도구를 사용할 수 있으면 좋겠지만 언어 특성상 영어를 위한 도구를 사용하기에는 적합하지 않다. 하지만 많은 사람들의 노력으로 개발된 한글 자연어 처리를 돕는 훌륭한 도구를 사용할 수있다. 그중 한글 자연어 처리에 많이 사용하는 파이썬 라이브버리 KoNLPy에 대해 알아보겠다. KoNLPy는 한글 자연어 처리를 쉽고 간결하게 처리할 수 있도록 만들어진 오픈소스 라이브러리다. 또한 국내에 이미 만들어져 사용되고 있는 여러 형태소 분석기를 사용할 수 있게 허용한다. 일반적인 어절 단위에 대한 토크나이징은 NLTK로 충분히 해결할 수 있으므로 형태소 단위에 대한 토크나이징에 대해 알아보도록 하겠다. 설치 리눅스 또는 macOS에서는 다음과 같이 pip를 이용해 간단하게 설치할 수 있다. 형태소 단위 토크나이징 한글 텍스트의 경우 형태소 단위 토크나이징이 필요할 떄가 있는데 KoNLPy에서는 여러 형태소 분석기를 제공하며, 각 형태소 분석기별로 분석한 결과는 다를 수 있다. 각 형태소 분석기는 클래스 형태로 되어 있고 이를 객체로 생성한 후 매서드를 호출해서 토크나이징할 수 있다. 형태소 분석 및 품사 태깅 형태소란 의미를 가지는 가장 작은 단위로서 더 쪼개지면 의미를 상실하는 것들을 말한다. 따라서 형태소 분석이란 의미를 가지는 단위를 기준으로 문장을 살펴보는 것을 의미한다. KoNLPy는 기존에 C, C++, Java 등의 언어를 통해 형태소 분석을 할 수 있는 좋은 라이브러리들을 파이썬 라이브러리로 통합해서 사용할 수 있록 하여 한국어 구문 분석을 쉽게 할 수 있도록 만들어진 라이브러리이다. KoNLPy에는 다양한 형태소 분석기들이 객체 형태로 포함돼 있으며 다음과 같은 각 형태소 분석기 목록이 있다. Hannanum Kkma Komoran Mecab Okt(Twitter) 모두 동일한 형태소 분석기능을 제공하는데, 각기 성능이 조금씩 다르다고 하니 직접 비교해보고 자신의 데이터를 가장 잘 분석하는 분석기를 사용하는 것이 좋다. (단, Mecab는 윈도우에서 사용할 수 없다.) 여기에서는 Okt 예로 들어 설명 하도록 하겠다. Okt는 원래 이름이 Twitter였으나 0.5.0 버전 이후부터 이름이 Okt로 바뀌었다. Okt 에서 제공되는 함수를 살펴보자. okt.morphs() 텍스트를 형태소 단위로 나눈다. 옵션으로 norm과 stem이 있다. norm은 문장을 정규화. stem은 각 단어에서 어간을 추출.(기본값은 둘다 False) okt.nouns() 텍스트에서 명사만 뽑아낸다. okt.phrases() 텍스트에서 어절을 뽑아낸다. okt.pos() 각 품사를 태깅하는 역할을 한다. 품사를 태깅한다는 것은 주어진 텍스트를 형태소 단위로 나누고, 나눠진 각 형태소를 그에 해당하는 품사와 함께 리스트화하는 것을 의미한다. 옵션으로 norm, stem, join이 있는데 join은 나눠진 형태소와 품사를 '형태소/품사' 형태로 같이 붙여서 리스트화한다. 다음 문장을 직접 각 함수에 적용해서 살펴보자. 모바일 게임은 재밌다 열심히 해서 만랩을 찍어야지~ ㅎㅎㅎ 어간 추출을 한 경우 의 어간인 로 추출된 것을 볼 수 있다. 이제 명사와 어절을 추츨헤 보자 nouns 함수를 사용한 경우에는 명사만 추출되었고 phrases 함수의 경우 어절 단위로 나뉘어서 추출 되었다. 품사 태깅을 하는 함수 pos를 사용해보자. join 옵션을 True로 설정 하면 형태소와 품사가 함께 나오는 것을 볼 수 있다. 경우에 따라 옵션을 설정하면서 사용하면 된다. KoNLPy 데이터 KoNLPy 라이브러리는 한글 자연어 처리에 활용할 수 있는 한글 데이터를 포함하고 있어 라이브러리를 통해 데이터를 바로 사용할 수 있다. kolaw 한국 법률 말뭉치. 'constitution.txt'파일 kobill 대한민국 국회 의안 말뭉치. 각 id값을 가지는 의안으로 구성. 파일은 '1809890.txt' 부터 '1809899.txt'까지로 구성. 라이브러리를 사용해 각 말뭉치를 불러오자. 위 데이터들을 가지고 여러 가지 한글 자연어 처리 문제를 연습하는 데 활용할 수 있다. 마치며 KoNLPy 홈페이지에 가보면 나름의 철학을 가지고 프로젝트를 진행하는 듯한 글을 볼 수 있다. > KoNLPy는 같은 기능을 하는 또 하나의 도구를 만들려는 것이 아닙니다. > 이 프로젝트에는 세 가지 철학이 있습니다. > - 사용법이 간단해야 한다. > - 누구나 쉽게 이용할 수 있어야 한다. > - "인터넷 민주주의는 효과적이다." 개인적으로 마음에 드는 글귀이다. 인터넷 민주주의를 위해 직접 참여도 가능하니 한국어 NLP에 관심이 많다면 아래 공식홈을 방문하여 살펴보길 바란다. KoNLPy 공식홈 : (https://konlpy-ko.readthedocs.io)
2019년 04월 18일9분NLP - 데이터 정제
Ai!nlp-pre Data Cleaning and Text Preprocessing 기계가 텍스트를 이해할 수 있도록 텍스트를 정제하고 신호와 소음을 구분하여 아웃라이어 데이터로 인한 오버피팅을 방지하기 위해서는 다음과 같은 처리를 해주어야 한다. HTML 태그, 특수문자, 이모티콘 처리 토근화(Tokenization) : 문장의 단어를 분리하는 단계 불용어(Stopword) 제거 : 자주 등장하지만 특별한 의미를 갖지 않는 단어 제거 어간 추출(Stemming) 및 음소표기법(Lemmatization) 정규 표현식 >텍스트 데이터 전처리 이해 > (입니닼ㅋㅋ -> 입니다 ㅋㅋ, 샤릉해 -> 사랑해) > 한국어를 처리하는 예시입니닼ㅋㅋㅋㅋㅋ -> 한국어를 처리하는 예시입니다 ㅋㅋ > >한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어Noun, 를Josa, 처리Noun, 하는Verb, 예시Noun, 입Adjective, 니다Eomi ㅋㅋKoreanParticle > (입니다 -> 이다) >한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어Noun, 를Josa, 처리Noun, 하다Verb, 예시Noun, 이다Adjective, ㅋㅋKoreanParticle > >한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어, 처리, 예시, 처리하는 예시 >(출처 : 트위터 한국어 형태소 분석기) BeautifulSoup 많이 쓰이는 파이썬용 파서로 html, xml을 파싱할때 주로 많이 사용한다. BeautifulSoup에는 기본적으로 파이썬 표준 라이브러리인 html파서를 지원하지만, lxml을 사용하면 성능 향상이 있다. BeautifulSoup(markup, ) html.parser : 빠르지만 유연하지 않기 때문에 단순한 html문서에 사용 lxml : 매우 빠르고 유연 xml : xml 파일에 사용 html5lib : 복접한 구조의 html에 대해서 사용.(속도가 느린편) ^^;" soup = BeautifulSoup(data, "html5lib") removetag = soup.gettext() result_text = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\ 문장에서 태그와 특수문자를 제거하기 위해 BeautifulSoup 와 정규표현식을 사용하였다. 토큰화 (Tokenization) 코퍼스(corpus)에서 토큰(token)이라 불리는 단위로 나누는 작업을 토큰화(Tokenization)라고 부른다. 토큰의 단위가 상황에 따라 다르지만, 보통 의미있는 단위로 토큰 정의한다. 토큰의 기준을 단어로 하는 경우 가장 간단한 방법은 띄어쓰기를 기준으로 자르는 것이다. I loved you. machine learning 에서 구두점을 제외시키고 토큰화 한 결과는 다음과 같다. 하지만 보통 토큰화 작업은 단순히 구두점이나 특수문자를 전부 제거하는 작업을 수행하는 것만으로 해결되지 않는다. 구두점이나 특수문자를 전부 제거하면 토큰이 의미를 잃어 버리는 경우가 발생하기도 하기때문이다. 띄어쓰기 단위로 자르면 사실상 단어 토큰이 구분되는 영어와 달리, 한국어는 띄어쓰기반으로는 단어 토큰을 구분하기 어렵다. >한국어는 단어의 다양한 의미와 높낮이 그리고 띄어쓰기가 어렵다 보니 잘못된 데이터를 많이 받게 되어 자연어처리를 하는데 있어 어려움이 많이 따른다. 하지만 다양한 곳에서 한국어 처리를 위한 형태소 분석기를 연구하고 있다. 얼마전 카카오에서도 카이라는 딥러닝 기술 기반의 형태소 분석기를 오픈소스로 공개 하였고 그외에 트위터, 코엔엘파이등 꽤 쓸만한 것들이 있다. 불용어 (Stopword) 일반적으로 코퍼스에서 자주 나타나는 단어로 학습이나 예측 프로세스에 실제로 기여를 하지 않는다. (조사, 접미사 - 나,나,은,는,이,가,하다,합니다....등) NLTK에는 17개의 언어에 대해 불용어가 정의되어 있다. 하지만 아쉽게도 한국어는...없다. 간단하게 10개 정도만 영어의 불용어를 보면, >결과 : ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', 'your'] 간단하다, 불용어 사전에 있는 단어들은 제거하면 된다. 한국어의 경우 불용어를 제거하는 방법으로는 위에서 언급한 형태소 분석 후 조사, 접속사 등을 제거할 수 있다. 어간 추출 (Stemming) 스태밍이라고 하는데, 어간이란 말은 단어의 의미를 담고 있는 단어의 핵심부분이라 생각하면 된다. 쉽게, 단어를 축약형으로 바꿔주는 작업이라고도 할 수 있다. 한국어가, 한국어는, 한국어처럼 -> 대표적으로 포터 스태머(PorterStemmer)와 랭커스터 스태머(LancasterStemmer)가 있는데 포터는 보수적이고 랭커스터는 좀 더 적극적이다. PorterStemmer >maxim >running >> run >runs >> run >run >> run LancasterStemmer >maxim >running >> run >runs >> run >run >> run 음소표기법 (Lemmatization) 언어학에서 음소표기법 (Lemmatization)은 단어의 보조 정리 또는 사전 형식에 의해 식별되는 단일 항목으로 분석 될 수 있도록 굴절 된 형태의 단어를 그룹화하는 과정이다. 어간 추출(Stemming)과는 달리 단어의 형태가 적절히 보존되는 양상을 보이는 특징이 있다. 하지만 그럼에도 의미를 알 수 없는 적절하지 못한 단어로 변환 하기도 하는데 음소표기법(Lemmatizer)은 본래 단어의 품사 정보를 알아야만 정확한 결과를 얻을 수 있기 때문이다. 품사정보가 보존된 형태의 기본형으로 변환. 단어가 명사로 쓰였는지 동사로 쓰였는지에 따라 적합한 의미를 갖도록 추출하는 것. >결과 : ['have', 'going', 'love', 'life', 'fly', 'dy', 'ha', 'starting'] 결과에서 보면 알수 있듯이 나 는 그 의미를 알수 없는 적절하지 못한 단어로 변환이 되었다. 하지만 dies나 has가 동사로 쓰였다는 것을 알려준다면 좀더 정확한 결과를 얻을 수 있게 된다. > 'die' > 'have' 음소표기법은 문맥을 고려하며, 수행했을 때의 결과는 해당 단어의 품사 정보를 보존한다. 하지만 어간 추출은 품사 정보가 보존이 되지 않는다. 마치며.. 이런 작업들이 갖고있는 의미는 눈으로 봤을 때는 서로 다른 단어들이지만, 하나의 단어로 일반화시킬 수 있다면 하나의 단어로 일반화시켜서 문서 내의 단어 수를 줄여보자는 것이고 자연어 처리에서 전처리의 지향점은 언제나 갖고 있는 코퍼스로부터 복잡성을 줄이는 일이다.
2019년 03월 20일10분OpenSSH Server를 WSL(Windows Subsystem for Linux)에서 구동하기
OpsWSL(Windows Subsystem for Linux)에서 OpenSSH Server를 띄우고 접속하는 방법에 대해 알아보겠다. sshd_config 먼저 Windows10에서 Windows Subsystem for Linux(WSL)을 설치한 후 CMD화면에서 bash명령으로 WSL에 진입한다. 를 수정해주자. > # ssh 접속 포트를 기입한다. 22는 윈도우 자체 내장 SSH서버가 이번호를 선점하고 있기 때문에 반드시 피해야 한다. > # SWL이 chroot()를 지원하지 않기 때문이라고 하는데 이 부분은 패스 했다. (안해도 되는듯..) > # SSH Key를 사용할 것이기 때문에 no로 설정 정상적으로 서버가 구동 되었다면 윈도우 방화벽에서 인바운드에 설정한 포트번호를 허용해주자. SSH Key인증을 이용한 접속 (Password 없이 접속) 위에서 설정한 서버에 클라이언트가 접속하기 위해 키를 생성하여 공개키 정보를 서버에 등록해주면 클라이언트에서는 별도의 패스워드 입력없이 접속할수 있게 된다. 먼저 키파일을 생성하자. 디렉토리에 idrsa, idrsa.pub, known_hosts 파일들이 생성된다. 자, 아래 명령으로 공개키 파일을 서버로 전송하자. 수동으로 복사해도 상관없지만 를 사용해 쉽게 전송하자. > 권한 오류가 발생한다.. 권한 오류가 발생하는 원인은 우리가 서버에서 설정한 에 를 로 설정했기 때문이다. 아직 key기반의 접속이 셋팅이 안된 상태이기 때문에 패스워드를 입력할 수 있도록 잠시 로 변경한 후 다시 시도 해보자. >Now try logging into the machine, with: "ssh '[user]@[host]'" >and check to make sure that only the key(s) you wanted were added. 복사가 정상적으로 이루어 졌다면 해당 서버의 를 로 다시 변경하자.(구지 다시 no로 변경할 필요는 없다. key기반의 접속이기때문에 자동으로 패스워드를 확인하는 절차는 패스되는 것 같다.)
2019년 03월 05일3분Angular Observable
Angular옵저버블은 ES7 릴리스에 포함될 비동기 데이터를 관리하기위한 새로운 표준이다. Angular는 이벤트 시스템과 HTTP 서비스에서 옵저버블을 광범위하게 사용한다. Observables 옵저버블은 시간이 지남에 따라 여러값을 가질 수 있는 지연 콜렉션이다. 1. Observables는 lazy하다. 지연 옵저버블을 뉴스 레터로 생각할 수 있다. 각 구독자(subscriber)마다 새로운 뉴스 레터가 만들어진다. 그 뉴스레터들은 구독자들에게만 보내고 다른 사람에게는 보내지 않는다. 2. Observables는 시간이 지남에 따라 여러 값을 가질 수 있다. 뉴스 레터 구독을 계속 열어두면, 매번 새로운 뉴스 레터를 받게된다. 발신자(sender)는 받은 시간을 결정하지만 받은 편지함에 곧바로 올 때까지 기다려야한다. 옵저버블과 프로미스간의 다른 중요한 차이점이 있는데, promise은 항상 오직 하나의 값만을 반환한다는 점이다. 또 하나는 옵저버블의 구독을 취소 할 수 있다는 점이다. 뉴스 레터를 더 이상 원하지 않으면 구독을 취소하면된다. 반면 프로미스는 취소 할 수 없다. 프라미스가 당신에게 건네지면, 그 프라미스의 resolve가 이미 진행되고 있으며, 일반적으로 프라미스의 resolve가 실행되는 것을 막을 수 있는 권한이 없다. Push vs pull observables를 사용할 때 이해해야 할 핵심 사항은 observables가 push한다는 것이다. (옵저버블은 push 시나리오를 따른다는 말) push와 pull은 데이터 생성자가 데이터 소비자와 커뮤니케이션하는 방법을 설명하는 두 가지 방식이다. Pull Pulling일 때 데이터 소비자는 데이터 생성자로부터 데이터를 가져 오는 시점을 결정한다. 생산자는 언제 데이터가 소비자에게 전달되는지를 알지 못한다. 모든 자바 스크립트 함수는 pull 시나리오를 사용한다. 함수는 데이터의 프로듀서이며 함수를 호출하는 코드는, 호출에서 하나의 반환 값을 “꺼내”(pull) 가져와 이를 소비한다. Push Pushing일 때, 다른 방향으로 동작한다. 데이터 생성자 (뉴스 레터 생성자)는 소비자 (뉴스 레터 구독자)가 데이터를 가져 오는 시점을 결정합니다. 프로미스는 오늘날 자바 스크립트에서 사용하는 가장 일반적인 push 방법입니다. 프로미스(생산자) 전달자는 등록된 콜백(소비자)에게 resolve된 값을 전달하고, 프로미스는 함수와는 달리 콜백에 그 값이 “푸시 (push)”되는 시기를 정확하게 결정한다. Observables는 JavaScript로 데이터를 푸시하는 새로운 방법이다. 옵저버블은 여러 값의 생산자로서 구독자에게 “푸시 (pushing)”한다. Angular에서 Observables Angular에서 HTTP 요청을 하면 옵저버블 형태로 반환을 한다. observable을 반환하는 fetchUsers 메서드를 사용하여 간단한 HttpClient를 만들었다. 어떤 종류의 리스트에 사용자를 표시하고 싶으므로 fetchUsers 메서드를 사용해서 해보자. 이 메소드는 옵저버블을 반환하기 때문에 우리는 그것을 구독해야 한다. Angular에서 우리는 두 가지 방식으로 Observable을 구독 할 수 있다. 방식 1 비동기 파이프를 사용하여 템플릿의 옵저버블을 구독하는 방법이 있다. 이로 인해 Angular는 컴포넌트의 생명주기 동안 구독을 처리한다. Angular는 자동으로 구독하고 구독취소한다. 비동기 파이프가 노출되야하므로 모듈에 “CommonModule”을 import하자. > 일반적으로 달러($)기호는 Stream의 약어로 쓰이기 때문에 변수이름 앞에 $를 사용하면 변수의 관찰 가능 여부를 쉽게 식별 할 수 있다. 방식 2 메소드를 사용하여 옵저버블을 구독한다. 데이터를 표시하기 전에 먼저 데이터에 뭔가 작업하기를 원한다면 편리할 수 있다. 단점은 구독을 직접 관리해야한다는 것다.(구독취소를 해주어야 함) 템플릿 로직이 꽤 비슷하다는 것을 알 수 있듯이, 2번 방식으로 간다면 구성 요소 논리는 훨씬 더 복잡해 질 수 있다. 일반적으로 방식1을 선택하는 것이 좋다. 가장 쉽고 구독을 수동으로 관리할 필요가 없다. Creating an observable yourself Angular가 제공한 일반적인 옵저버블을 다루는 방법을 알았으므로 옵저버블을 어떻게 생성하는지 보자. observables는 새로운 Observable() 호출을 사용하여 만든 다음 observer에 가입하고 next()를 호출하여 실행하고 unsubscribe()를 호출하여 삭제한다. observable 만들기 observables를 만드는 것은 쉽다. 새로운 Observable()을 호출하고 옵저버를 나타내는 하나의 인수를 전달하면됩니다. 그러므로 저는 보통 그것을 “observer”라고 부른다. 옵저버블 구독하기 옵저버블은 구독하지 않으면 아무 일도 일어나지 않을 것이다. 옵저버를 구독 할 때 subscribe()를 호출 할 때마다 옵저버블에 독립 설정이 실행된다. 구독 요청은 동일한 옵저버블에 대한 여러 구독자간에 공유되지 않는다. 옵저버블 실행하기 observables 안의 코드는 observables의 실행을 나타낸다. 옵저버블을 만들 때 주어진 매개 변수(observer)에는 옵저버블의 구독자에게 데이터를 보낼 수있는 세 가지 함수가 있다. next: Number나 Array나 객체같은 여러 값을 subscribers에게 보낸다. error: 자바스크립트 에러나 예외값을 보낸다. complete : 어떤 값도 보내지 않는다. 콜은 구독자에게 실제로 데이터를 전달할 때 가장 일반적이다. 옵저버블의 실행 중에는 의 무한 호출이있을 수 있지만 또는 가 호출되면 실행이 중지되고 더 이상 데이터가 subscribers에게 전달되지 않는다. 옵저버블 처분 옵저버블의실행은 무한한 시간 동안 실행될 수 있기 때문에, 실행을 막을 수있는 방법이 필요하다. 각 구독자마다 각 실행이 실행되기 때문에, 메모리와 컴퓨팅 성능이 낭비, 즉 더 이상 데이터가 필요없는 구독자는 구독을 멈추는 것이 중요하다. 옵저버블을 구독할 때, 진행중인 실행을 취소하면 구독이 반환된다. 실행을 취소하려면 를 호출하면 된다.
2019년 02월 17일12분카프카(Kafka) 설치 및 클러스터 구성
DatalakeKafka를 설치하고 간단하게 클러스터 환경을 구성하여 Producer 및 Consumer 테스트를 해보자. 먼저 가상머신을 이용해 다음 그림과 같이 클러스터 환경을 구성하여 테스트를 진행할 것이다. !img 이 글에서는 3대의 서버를 생성하였지만 1대의 서버로 포트를 다르게 하여 구성할 수도 있다. 또한 Kafka와 Zookeeper서버를 동일한 장비에 구축하였지만 실무에서는 별도로 구축하는 것이 좋다. Kafka 다운로드 및 설치 다운로드(각 버전에 대해서는 https://kafka.apache.org/downloads를 참고하자.) 압축해제 및 경로이동 Kafka의 동작은 Zookeeper에 의해 관리가 되기 때문에 Zookeeper 없이는 Kafka를 구동할 수 없다. 이 때문에 Kafka를 다운로드 하면 Zookeeper도 함께 들어있다. 물론 별도로 최신버전의 Zookeeper를 다운받아 사용해도 되지만, Kafka에 들어있는 Zookeeper는 Kafka버전과 잘 동작하는 검증된 버전이므로 패키지 안에 있는 Zookeeper의 사용을 권장한다. Zookeeper 설정 각 인스턴스에 설치된 Kafka의 config/zookeeper.properties 파일은 하나의 Zookeeper를 실행하는데 쓰이는 설정 파일이다. 이 말은 zookeeper1.properties, zookeeper2.properties, zookeeper3.properties 이런식으로 여러개의 설정파일을 만들고 하나의 장비에서 다중으로 실행할 수 있다는 의미이다. 설정파일을 다음과 같이 3대의 서버에 동일하게 추가하자. 새로 추가한 설정값은 클러스터를 구성하는데 필요한 설정 값들안데 여기서 주의할 점은 모든 Zookeeper 서버들은 동일한 변수 값을 가지고 있어야 한다. initLimit 팔로워가 리더와 초기에 연결하는 시간에 대한 타임아웃 syncLimit 팔로워가 리더와 동기화 하는데에 대한 타임아웃. 즉 이 틱 시간안에 팔로워가 리더와 동기화가 되지 않는다면 제거 된다. 이 두값은 dafault 기본값이 없기 때문에 반드시 설정해야 하는 값이다. 그리고 server.1,2,3의 숫자는 인스턴스 ID이다. ID는 dataDir=/tmp/zookeeper 폴더에 myid파일에 명시가 되어야 한다. /tmp/zookeeper 디렉토리가 없다면 생성하고 myid 파일을 생성하여 각각 서버의 고유 ID값을 부여해야 한다. (그 외 자세한 설정정보를 알고 싶다면 주키퍼 가이드문서를 참고하자.) 1 서버 (192.168.137.101) 2 서버 (192.168.137.102) 3 서버 (192.168.137.103) 이제 Zookeeper를 구동하기 위한 설정은 끝~ Kafka 설정 Kafka의 config/server.properties 파일은 하나의 Kafka를 실행하는데 쓰이는 설정 파일이다. Zookeeper와 마찬가지로 여러개의 설정파일을 만들고 다중 실행을 할 수 있다. 설정파일 config/server.properties에 3대 서버 각 환경에 맞는 정보를 입력해 준다. 1 서버 (192.168.137.101) 2 서버 (192.168.137.102) 3 서버 (192.168.137.103) 별도 파일(/tmp/zookeeper/myid)에 인스턴스 ID를 명시해야 하는 Zookeeper와는 달리 Kafka는 설정파일안에 broker.id라는 항목이 있다. 그리고 zookeeper.connet라는 항목에는 Zookeeper인스턴스들의 정보를 입력해준다. Kafka를 구동하기 위한 설정은 끝났다. 클러스터 구성을 위한 인스턴스들의 정보를 입력해 주는것이 거의 대부분의 설정이다. 그외 설정 파일에 대한 상세한 내용은 공식 홈페이지의 Broker Configs를 참고하길 바란다. default설정값을 확인하고 변경하고자 하는 값들은 설정파일에 명시를 해주면 된다. Zookeeper 및 Kafka 서버 구동 Kafka를 구동하기 위해 먼저 Zookeeper를 구동 한다음 이후 Kafka를 구동해야 한다. 3대의 서버에 Zookeeper와 Kafka가 정상적으로 구동이 되었다면 다음과 같이 starting 메시지를 확인할 수 있을 것이다. *starting (kafka.server.KafkaServer) 이제 기본적인 클러스터 환경을 구성하고 서버를 구동시켰다. Kafka에서는 bin폴더 아래 제공되는 스크립트 파일을 이용해 Topic을 관리하고 Producer,Consumer를 테스트 해볼수 있다. 기본적인 몇가지 스크립트 명령을 이용해 Topic을 생성해 보고 Producer 메시지를 저장하고 Consumer 메시지를 읽어오는것을 확인해 보도록 하자. Topic 관리 1. Topic생성 GameLog, GameLog2, GameLog3 세개의 Topic을 생성해보자.(replication-factor:3, partitions : 1) 출력 Created topic "GameLog" 2. Topic 리스트 확인 출력 GameLog GameLog2 GameLog3 3. Topic 삭제 출력 Topic GameLog3 is marked for deletion 4. Topic 상세 정보 확인 출력 PartitionCount:1 ReplicationFactor:3.. 토픽을 생성했으면 해당 토픽에 메시지를 생산하고 소비하는것을 직접 확인해보자. 메시지 생산 및 소비 1.Producer 메시지 생산하기 !img 2.Consumer 메시지 소비하기 !img from-beginning 옵션은 해당 topic의 맨 처음 메시지부터 소비하겠다는 의미이다. 그림에서 보는 것과 같이 Producer에서 메시지를 입력하면 Consumer에서 해당 메시지를 읽어오는 것을 확인 할 수 있다. 지금까지 Kafka 클러스터를 직접 구축하여 메시지 생산하고 소비하는 것을 간단하게 해보았다.
2019년 02월 14일8분Jupyter Notebook 설치 및 셋팅
Ai머신러닝 관련 코드를 작성해보기 위해 환경을 세팅해보자. 다른 방법들도 많겠지만 가장 많이 쓰는 주피터 노트북을 설치하고 파이썬 패키지를 어떻게 관리할 것인지를 살펴보도록 하자. 주피터 노트북 주피터 노트북(jupyter notebook)은 웹 브라우저에서 파이썬 코드를 작성하고 실행까지 해볼 수 있는 REPL방식의 개발도구이다. 머신러닝이나 데이터분석 용도로 파이썬을 사용하는 사람들이 사용하는 툴로써 가벼우며 코드를 실행하고 수정하기가 간편하다. 또한 notebook형태로 파일이 공유가 가능하다. > REPL(Read–Eval–Print Loop) : 명령어를 한줄씩 입력하며 실행 상황을 지켜보는 방식(대화형) 먼저 파이썬을 설치하자. 파이썬이 이미 설치가 되어있다면 생략 하자. 파이썬 pip를 설치한 후 로 주피터 노트북을 설치 할 수 있지만 일반적으로 를 설치하면 이 함께 설치되어 주피터를 사용할 수 있다. 물론 를 이용하면 원하는 패키지만 그떄 그때 설치하여 사용할 수는 있지만, 아나콘다를 설치하면 데이터 처리 및 분석에 필요한 패키지가 모두 포함되어 설치가 되기 때문에 약간 설치시간이 오래 걸리긴 하지만 아나콘다를 사용하는 것을 추천한다. 아나콘다(Anaconda) 설치 이 글에서는 Ubuntu18.04 기준으로 설명하도록 하겠다. 또한 최신버전을 위해 반드시 아나콘다 사이트에서 최신 URL을 확인하여 설치를 진행하길 바란다. >계속 엔터를 눌러 설치한다(bashrc등록 여부는 Y로 입력해준다 - 그렇지 않으면 수동설정) 아나콘다(Anaconda) 공식홈 참고 설치 확인 만약 삭제하길 원한다면... 주피터 노트북 실행 !jupyter-run > 화면에 출력된 으로 접속하면 된다. > ( 명령으로 파일을 생성하면 해당 파일에서 세부설정이 가능하다.) 접속화면 !jupyter 우측 상단에 New > Python3를 선택하면 새로운 창이 생성된다. !jupyter 이제 웹상에서 파이썬 코드를 작성하고 실행 할 수 있다. 물론 matplotlib 패키지를 이용하여 가공된 데이터를 그래프로 표현 할 수 있다. !jupyter 주피터 노트북은 개인 로컬환경에 설치하여 직접 띄울수도 있지만, Microsoft에서는 azure notebook, Google에서는 Colab에서 클라우드상의 주피터환경을 제공해주고 있으니 간단한 학습용로라면 이를 활용하는 방법도 좋을 것이다. 공짜다! Microsoft Azure Notebooks Google Colab (주피터 노트북 사용법에 대한 부분은 다루지 않겠다.) 더 살펴보자... 아나콘다 가상환경 개발을 위한 파이썬 패키지 가상환경을 별도로 만들고 싶다면 conda에서 지원하는 명령어로 사용이 가능하다. 만약 아나콘다를 사용하지 않고 를 사용한다면 로 가상환경을 관리 할 수 있다. >가상환경을 선택하여 활성화를 하게 되면 와 같이 프롬프트 앞에 해당 가상환경명이 표시가 된다. 이후 패키지를 설치하게 되면 활성화된 가상환경에만 패키지가 설치가 된다. 다시 가상환경을 비활성화하거나 삭제하려면, 파이썬에서는 한 라이브러리에 대해 하나의 버전만 설치가 가능한데 이는 여러개의 프로젝트를 진행하게 되면 문제가 될 수 있다. 작업을 바꿀때마다 다른 버전의 라이브러리를 설치해야 해야 하는 번거러움을 방지하기 위해 독립된 가상환경이 필요한 것이다. 아나콘다에 패키지 설치 가상환경을 활성화 하지 않고 가상환경명을 지정하여 패키지를 설치 할 수도 있다. 라는 가상환경에 를 설치 및 삭제를 해보자. 주피터 설치 라는 가상환경을 활성화 하고 jupyter를 설치해보자. 주피터 확장기능 설치 주피터 확장기능을 사용하면 좀더 편리한 기능들을 사용할 수 있다. > 확장을 활성화 하려면 을 클릭하거나 http://localhost:8888/nbextensions에서 확인 할 수 있다. 주피터 font 수정 font가 가독성이 좀 떨어진다. custom.css를 추가하여 css를 적용할 수 있다. 주피터 상세 설정 기본 디렉토리, 토큰, 브라우저옵션 등 상세한 설정을 제어 하고 싶다면 config파일을 생성하여 해당 파일의 옵션값들을 수정해주면 된다. 결과 Writing default config to: /home/uss/.jupyter/jupyternotebookconfig.py Matplotlib 설정 matplotlib 주요 설치 정보 우분투에 폰트는 에 있다. 글꼴 설치 > 에 nanum 폰트 파일이 설치가 된다. 설치한 nanum폰트를 matplotlib에 복사하고 matplotlib의 폰트 캐시를 삭제하자. > 설치 위치는 /usr/local/lib/python3.?/dist-packages/matplotlib가 기본적이지만 matplotlib.file 명령으로 설치위치를 확인하자.(가상환경의 경우 위치가 다를 수 있음) > 캐시 위치는 보통 사용자 디렉토리에 위치한다. 폰트 지정 matplotlib.rcParams을 통해 matplotlib의 다양한 설정값을 읽거나 지정할 수 있다. [(f.name, f.fname) for f in matplotlib.font_manager.fontManager.ttflist if 'Nanum' in f.name] 으로 Nanum폰트명을 확인 할 수 있다. 폰트 설정파일 매번 폰트를 지정하는것이 번거롭다면 matplotlibrc 파일에 설정하면 된다. > 레이블에 '-'가 있는 경우 유니코드의 '-'문자를 그대로 출력하면 '-' 부호만 깨져 보인다. 이를 방지하기 위해 'axes.unicode_minus' 옵션을 False로 지정한다. 이제 jupyter notebook를 재구동하고 그래프에 한글이 정상적으로 표시가 되는지 확인해보자. 윈도우에서 아나콘다를 사용한다면.. 1. matplotlib 설정파일에 설정 추가 (matplotlib.matplotlib_fname()) font.family : NanumGothic font.size : 10.0 axes.unicode_minus : False 2. .ttf 폰트를 matplotlib으로 복사한다. 아나콘다를 사용하는 경우 보통 아래 디렉토리이다.(NanumGothic 추천) C:\Users\(사용자명)\Anaconda3\Lib\site-packages\matplotlib\mpl-data\fonts\ttf 3. matplotlib cache 폴더의 내용 삭제 matplotlib.get_cachedir() 4. 커널 재시작(주피터 노트북 재시작) 참고 Jupyter Notebook
2019년 02월 14일12분NLP - 텍스트 분류 (전처리) -
Ai캐글(Kaggle)의 "Bag of Words meets bag of popcorns" (https://www.kaggle.com/c/word2vec-nlp-tutorial) 워드팝콘 데이터의 처리 과정 > 케글 데이터 불러오기 -> EDA -> 데이터정제 -> 모델링 데이터정제 HTML 및 문장 보호 제거 불용어 제거 단어 최대 길이 설정 단어 패딩 백터 표상화 NanumGothic.ttf ratings.txt test_id.npy labeledTrainData.tsv ratingstest.txt trainclean.csv labeledTrainData.tsv.zip ratingstrain.txt traininput.npy nsmctestinput.npy sampleSubmission.csv train_label.npy nsmctestlabel.npy testData.tsv unlabeledTrainData.tsv nsmctraininput.npy testData.tsv.zip unlabeledTrainData.tsv.zip nsmctrainlabel.npy test_clean.csv id sentiment review "5814_8" 1 "With all this stuff going down at the moment with MJ i've started listening to his music, watching the odd documentary here and there, watched The Wiz and watched Moonwalker again. Maybe i just want to get a certain insight into this guy who i thought was really cool in the eighties just to maybe make up my mind whether he is guilty or innocent. Moonwalker is part biography, part feature film which i remember going to see at the cinema when it was originally released. Some of it has subtle messages about MJ's feeling towards the press and also the obvious message of drugs are bad m'kay.Visually impressive but of course this is all about Michael Jackson so unless you remotely like MJ in anyway then you are going to hate this and find it boring. Some may call MJ an egotist for consenting to the making of this movie BUT MJ and most of his fans would say that he made it for the fans which if true is really nice of him.The actual feature film bit when it finally starts is only on for 20 minutes or so excluding the Smooth Criminal sequence and Joe Pesci is convincing as a psychopathic all powerful drug lord. Why he wants MJ dead so bad is beyond me. Because MJ overheard his plans? Nah, Joe Pesci's character ranted that he wanted people to know it is he who is supplying drugs etc so i dunno, maybe he just hates MJ's music.Lots of cool things in this like MJ turning into a car and a robot and the whole Speed Demon sequence. Also, the director must have had the patience of a saint when it came to filming the kiddy Bad sequence as usually directors hate working with one kid let alone a whole bunch of them performing a complex dance scene.Bottom line, this movie is for people who like MJ on one level or another (which i think is most people). If not, then stay away. It does try and give off a wholesome message and ironically MJ's bestest buddy in this movie is a girl! Michael Jackson is truly one of the most talented people ever to grace this planet but is he guilty? Well, with all the attention i've gave this subject....hmmm well i don't know because people can be different behind closed doors, i know this for a fact. He is either an extremely nice but stupid guy or one of the most sickest liars. I hope he is not the latter." | | id | sentiment | review | |-|-|-|-| | 0 | 5814_8 | 1 | With all this stuff going down at the moment w... | | 1 | 2381_9 | 1 | "The Classic War of the Worlds" by Timothy Hi... | | 2 | 7759_3 | 0 | The film starts with a manager (Nicholas Bell)... | | 3 | 3630_4 | 0 | It must be assumed that those who praised this... | | 4 | 9495_8 | 1 | Superbly trashy and wondrously unpretentious 8... | 데이터는 "id", "sentiment", "review"로 구분되어 있어며, 각 리뷰('review')에 대한 감정('sentiment')이 긍정(1), 부정(0)인지 나와있다. 이제 다음과 같은 순서로 데이터 분석을 해보자. 1. 데이터 크기 2. 데이터 개수 3. 각 리뷰 문자 길이 분포 4. 많이 사용된 단어 5. 긍정, 부정 데이터의 분포 6. 각 리뷰의 단어 개수 분포 7. 특수문자 및 대문자, 소문자 비율 데이터 크기 파일 크기 : labeledTrainData.tsv 33.56MB testData.tsv 32.72MB unlabeledTrainData.tsv 67.28MB 데이터 개수 전체 학습 데이터의 개수: 25000 각 리뷰의 문자 길이 분포 0 2302 1 946 2 2449 3 2245 4 2231 Name: review, dtype: int64 Text(0, 0.5, 'Number of review') !png figsize: (가로, 세로) 형태의 튜플로 입력 bins: 히스토그램 값들에 대한 버켓 범위 range: x축 값의 범위 alpha: 그래프 색상 투명도 color: 그래프 색상 label: 그래프에 대한 라벨 대부분 6000 이하 그중에서도 2000 이하에 분포되어 있음을 알 수 있다. 그리고 일부 데이터의 경우 이상치로 10000 이상의 값을 가지고 있다. 길이데 대해 몇가지 통계를 확인해 보자 리뷰 길이 최대 값: 13708 리뷰 길이 최소 값: 52 리뷰 길이 평균 값: 1327.71 리뷰 길이 표준편차: 1005.22 리뷰 길이 중간 값: 981.0 리뷰 길이 제 1 사분위: 703.0 리뷰 길이 제 3 사분위: 1617.0 리뷰의 길이가 히스토그램에서 확인했던 것과 비슷하게 평균이 1300정도 이고, 최댓값이 13000이로 확인이 된다. !png labels : 입력한 데이터에 대한 라벨 showmeans : 평균값을 마크함 데이터의 길이가 대부분 2000 이하로 평균이 1500 이하인데, 길이가 4000 이상인 이상치 데이터도 많이 분포되어 있는 것을 확인할 수 있다. 이제 리뷰에서 많이 사용된 단어로 어떤 것이 있는지 알아보자. 많이 사용된 단어 (-0.5, 799.5, 599.5, -0.5) !png 워드 클라우드를 통해 살펴보면 가장 많이 사용된 단어는 br로 확인이 된다. br은 HTML 태그 이기때문에 이 태그들을 모두 제거 하는 전처리 작업이 필요하다. 긍정/부정 데이터의 분포 !png 거의 동일한 개수로 분포되어 있음을 확인할 수 있다. 좀더 정확한 값을 확인해 보자. 긍정 리뷰 개수: 12500 부정 리뷰 개수: 12500 각 리뷰의 단어 개수 분포 각 리뷰를 단어 기준으로 나눠서 각 리뷰당 단어의 개수를 확인해 보자. 단어는 띄어쓰기 기준으로 하나의 단어라 생각하고 개수를 계산한다. 우선 각 단어의 길이를 가지는 변수를 하나 설정하자. Text(0, 0.5, 'Number of reviews') !png 대부분의 단어가 1000개 미만의 단어를 가지고 있고, 대부분 200개 정도의 단어를 가지고 있다. 리뷰 단어 개수 최대 값: 2470 리뷰 단어 개수 최소 값: 10 리뷰 단어 개수 평균 값: 233.79 리뷰 단어 개수 표준편차: 173.74 리뷰 단어 개수 중간 값: 174.0 리뷰 단어 개수 제 1 사분위: 127.0 리뷰 단어 개수 제 3 사분위: 284.0 통계를 살펴보면 평균이 233개, 최댓값은 2470개의 단어를 가지고 있다. 그리고 3사분위 값이 284개로 리뷰의 75%가 300개 이하의 단어를 가지고 있음을 확인 할 수 있다. 특수문자 및 대/소문자 비율 물음표가있는 질문: 29.55% 마침표가 있는 질문: 99.69% 첫 글자가 대문자 인 질문: 92.84% 대문자가있는 질문: 99.59% 숫자가있는 질문: 56.66% 결과를 보면 대부분 마침표를 포함하고 있고, 대문자도 대부분 사용하고 있다. 따라서 전처리 과정에서 대문자의 경우 모두 소문자로 바꾸고 특수 문자의 경우 제거해야 한다. 이 과정은 학습에 방해가 되는 요소들을 제거하기 위함이다. 데이터 전처리 어떤 방향으로 전처리해야 할지 결정하기 위해 데이터 하나를 자세히 보자. "With all this stuff going down at the moment with MJ...bad m'kay....I hope he is not the latter." 우선 과 같은 HTML 태그와 , 같은 특수문자가 포함되어 있는것을 확인할 수 있는데, 이는 일반적으로 문장의 의미에 크게 영향을 주지 않기 때문에 BeautifulSoup과 re.sub을 이용해 제거하자. With all this stuff going ... I hope he is not the latter ['stuff', 'going', 'moment', 'mj', 'started', 'listening',,,'hope', 'latter'] stuff going moment mj started listening ... iars hope latter 전체 데이터에 적용하기 위해 함수화 하자. 'stuff going moment mj started listening ... sickest liars hope latter' 위와 같이 하면 각 리뷰가 텍스트가 아닌 인덱스의 벡터로 구성될 것이다. [404, 70, 419, 8815, 506, 2456, 115, 54, 873, ,,, ,18688, 18689, 316, 1356] 전체 데이터가 인덱스로 구성됨에 따라 각 인덱스가 어떤 단어를 의미하는지 확인할 수 있어야 하기 때문에 단어 사전이 필요하다. 총 74,000개 정도의 단어 이다. 단어 사전뿐 아니라 전체 단어의 개수도 이후 모델에서 사용되기 때문에 저장해 둔다. Shape of train data: (25000, 174) 패딩 처리를 위해 함수를 사용하였다. 최대 길이를 174로 설정한 것은 앞서 단어 개수의 통계를 계산했을 때 나왔던 중간값이다. 보통 평균이 아닌 중간값을 사용하는 경우가 많은데, 일부 이상치 데이터로 인해 평균값이 왜곡될 우려가 있기때문이다. 패딩 처리를 통해 데이터의 형태가 25,000개의 데이터가 174라는 길이를 동일하게 가지게 되었다. Shape of label tensor: (25000,) numpy 배열로 만든 후 라벨의 형태를 확인해 보면 길이가 25,000인 벡터임을 확인할 수 있다. 이렇게 라벨까지 numpy 배열로 저장하면 모든 전처리 과정이 끝난다. 원본 데이터를 벡터화하는 과정을 그림을 통해 이해해 보자. !oorigin-vector 이제 전처리한 데이터를 이후 모델링 과정에서 사용하기 위해 저장을 하도록 하자. test 데이터도 동일하게 저장하자. 다만 test 데이터는 라벨 값이 없기 때문에 라벨은 따로 저장하지 않아도 된다. 추가로 test 데이터네 대해 저장해야 하는 값이 있는데 각 리뷰 데이터에 대해 리뷰에 대한 값을 저장해야 한다. test 데이터를 전처리할 떄 한 가지 중요한 점은 토크나이저를 통해 인덱스 벡터로 만들 때 토크나이징 객체로 새롭게 만드는 것이 아니라, 이다. 만약 새롭게 만들 경우 Train 데이터와 Test 데이터에 대한 각 단어들의 인덱스가 달라져서 모델에 정상적으로 적용할 수 없기 때문이다. 지금까지의 결과를 아래와 같은 파일들에 각각 저장을 하였다. >단어 인덱스 사전 및 개수 : data_configs.json >훈련 데이터 : traininput.npy, trainlabel.npy, train_clean.csv >테스트 데이터 : testinput.npy, testclean.csv, test_id.npy 이제 저장된 데이터를 이용해 다음 스탭을 진행하도록 하겠다.
2019년 01월 30일20분ML - 머신러닝?!
Ai인공지능, 머신러닝, 딥러닝에 대해 자세히는모르지만 부분 한번쯤을 어보았을 것이다. 분명 3가지는 차이가 있으며 어떤 차이가 있는지부터 알아보자. 머신러닝은 말 그데로 "기계가 학습?" 그럼 인공지능과 딥러닝은? 마찬가지로 기계학습이라 해도 틀린것은 아니다. 인공지능은 매우 포괄적인 개념으로 가장 많이 사용되는 대중적인 단어이다. 특정 기술 분야 뿐아니라 지능적인 요소를 가진 모든 부분에 대해 부르는 이름이다. 예를들어 게임상의 Bot이나 음성인식을 이용한 검색, 통계를 기반으로한 예측시스템들도 인공지능이라 부른다. 반면 머신러닝은 인공지능 안에서의 특정분야를 지칭하는 단어이다. 그리고 그안에 파생된 기술도 딥러닝 뿐만 아니라 다른 많은 기술도 있다. 사실 기계학습, 인공지능에 대한 연구는 예전부터 존재했지만 발전이 없었고 단지 소수에 연구원들에 의한 주제였기에 대중화 될 수 없었다고 한다. 하지만 풍부한 데이터의 확보, 컴퓨팅 성능향상, 오픈소스 라이브러리로 인해 많은 개발자들이 인공지능 연구에 참여하게 되면서부터 크게 부각이 되기 시작했다. 머신러닝 머신러닝은 데이터로부터 학습하도록 컴퓨터를 프로그래밍하는 과학이다. 스팸필터로 예를 들면 스팸필터는 스팸메일과 일반메일의 샘플을 이용해 스팸메일 구분법을 배울수 있는 머신러닝 프로그램이다. 왜 머신러닝을 사용할까? 스팸 필터를 예로 전통적인 프로그래밍 기법과 비교해서 살펴보자. 스팸필터를 만들기 위해서는.. > 1. 스팸에 어떤 단어즐이 주로 나타나는지? '4U', '신용카드', '무료' 등등 > 2. 발견한 각 패턴을 감지하는 알고리즘을 작성하여 메일을 스팸으로 분류 > 3. 프로그램을 테스트하고 충분한 성능이 나올 때까지 1,2단계를 반복 전통적인 접근 방법에는 문제가 단순하지 않아 규칙이 복잡해지기 때문에 유지보수가 매우 힘들다. 반면 머신러닝 기법을 이용한다면 일반 메일에 비해 스팸에 자주 나타나는 패턴을 감지하여 특정 단어나 구절이 스팸 메일을 판단하는 데 좋은 기준인지 자동으로 학습한다. 따라서 유지보수가 쉬우며 정확도가 더 높다. 만약 스팸 메일 발송자가 '4U'를 포함한 모든 메일이 차단된다는 것을 안다면 '4U' 대신 'For U'를 쓰기 시작할지도 모른다. 전통적인 프로그래밍 방식의 스팸 필터는 'For U'메일을 구분하기 위해 수정이 필요하며 단어를 바꾸면 새로운 규칙을 계속 추가해 주어야 한다. 하지만 머신러닝 기반의 스팸 필터는 사용자가 스팸으로 지정한 메일에 유동 'For U'가 자주 나타나는 것을 자동으로 인식하고 별도 작업 없이도 자동으로 이 단어를 스팸으로 분류한다. !ori-ml 따라서 머신러닝 기술을 적용해서 대용량 데이터를 분석하면 겉으로는 보이지 않던 패턴을 발견할 수 있다. 머신러닝 시스템의 종류 머신러닝 시스템은 크게 세가지로 구분된다. > - 지도학습(Supervised Learning), > - 비지도학습(Unsupervised Learning), > - 강화학습(Reinforcement Learning) !ml-2 그림 출처 : (https://www.techleer.com/articles/203-machine-learning-algorithm-backbone-of-emerging-technologies/) 각각의 어떤 특징들을 가지고 있는지 살펴보도록 하자. 지도 학습(Supervised Learning) "지도학습은 훈련 데이터(Training Data)로부터 하나의 함수를 유추해내기 위한 기계학습의 한 방법이다." - 위키피디아 정답이 있는 데이터를 기반으로 모델을 만들어 새로운 데이터가 들어왔을때 정답을 맞추는 것이다. 지도학습은 크게 분류(Classification)과 회귀(Regression) 두가지로 분류할 수 있다. 분류 : 이진분류(예 or 아니오), 다중분류(고양이, 사자, 새..) 회귀 : 어떤 데이터들의 특징을 토대로 값을 예측(결과값은 실수, 키가 170면 몸게는?) 다음과 같이 지도학습에 사용되는 알고리즘이 있다. k-최근접 이웃 (k-Nearest Neighbors) 선형 회귀 (Linear Regression) 로지스틱 회귀 (Logistic Regression) 서포트 벡터 머신 (Support Vector Machines (SVM)) 결정 트리 (Decision Tree) 와 랜덤 포레스트 (Random Forests) 신경망 Neural networks 지도학습 알고리즘에 대해서는 다음에 다루도록 하겠다. 비지도 학습(Unsupervised Learning) 비지도 학습에 대해 이야기 하기 전에 먼저 지도 학습과 비지도 학습을 비교한 그림을 보자. !supervised-learning-diagram "데이터가 어떻게 구성되었는지를 알아내는 문제의 범주에 속한다." - 위키피디아 지도학습과는 다르게 정답이 없는 데이터를 가지고 컴퓨터를 학습시키는 방법이다. 즉 데이터의 형태(특징?)으로 학습을 진행한다. (흔히 알고 있는 군집화..) 그리고 데이터를 이용해 컴퓨터는 스스로 어떤 관계를 찾아낸다. 비지도학습도 마찬가지로 다음과 같은 알고리즘이 있다 군집 (Clustering) k-평균k-Means 계층 군집 분석Hierarchical Cluster Analysis (HCA) 기댓값 최대화Expectation Maximization 시각화 (Visualization)와 차원 축소(Dimensionality reduction) 주성분 분석 (Principal Component Analysis (PCA)) 커널 (kernel PCA) 지역적 선형 임베딩 (Locally-Linear Embedding (LLE)) 연관 규칙 학습 (Association rule learning) 어프라이어리(Apriori) 이클렛(Eclat) 강화학습(Reinforcement Learning) !reinforce (그림 출처 : Hands-On Machine Learning 도서 - 한빛미디어) 강화학습은 상과 벌이라는 보상을 주며 하도록 학습하는 방식이다. 주로 제어나 게임 플레이 등 상호작용을 통해서 최적의 동작을 학습해야 할 때 많이 사용된다. 데이터가 먼저 주어지는 지도학습이나 비지도학습과 달리 강화학습은 환경이 있고 에이전트가 그 환경 속에서 어떤 액션을 취하고 그 액션에 따른 어떤 보상을 얻게 되면서 학습이 진행된다. 에이전트가 보상을 최대화 하도록 하면서 학습이 진행되기 때문에 데이터 수집까지 포함하는 동적인 개념의 학습을 하게 된다. 우리의 인생으로 비유를 하자면 마치 시행 착오를 겪으며 경험이 쌓이고 경험을 토데로 옳고 그름을 판단을 수 있게 되는 것과 마찬가지다.
2019년 01월 30일10분카프카(Kafka)의 이해
Datalake대용량 게임로그 수집을 위해 Elastic Stack을 도입하게 되었는데, 중간에 버퍼역할(메시지큐)을 하는 Kafka에 대서 알아보려고 한다. !img 메시지큐? 메시지 지향 미들웨어(Message Oriented Middleware: MOM)은 비동기 메시지를 사용하는 다른 응용프로그램 사이의 데이터 송수신을 의미하는데 MOM을 구현한 시스템을 메시지큐(Message Queue:MQ)라 한다. 카프카란? 분산형 스트리밍 플랫폼(A distributed streaming platform)이다. LinkedIn에서 여러 구직 및 채용 정보들을 한곳에서 처리(발행/구독)할 수 있는 플래폼으로 개발이 시작 되었다고 한다. (발행/구독: pub-sub은 메시지를 특정 수신자에게 직접적으로 보내주는 시스템이 아니고, 메시지를 받기를 원하는 사람이 해당 토픽(topic)을 구독함으로써 메시지를 읽어 올 수 있다.) 카프카의 특징 대용량 실시간 로그처리에 특화되어 설계된 메시징 시스템으로 TPS가 매우 우수하고, 메시지를 메모리에 저장하는 기존 메시징 시스템과는 달리 파일에 저장을 하는데 그로 인해 카프카를 재시작해도 메시지 유실 우려가 감소된다. 기본 메시징 시스템(rabbitMQ, ActiveMQ)에서는 브로커(Broker)가 컨슈머(consumer)에게 메시지를 push해 주는 방식인데, 카프카는 컨슈머(Consumer)가 브로커(Broker)로부터 메시지를 직접 가져가는 PULL 방식으로 동작하기 때문에 컨슈머는 자신의 처리 능력만큼의 메시지만 가져와 최적의 성능을 낼 수 있다. 대용량처리에 특화 되었다는 것은 아마도 이러한 구조로 설계가 되어 가능하게 된게 아닌가 싶다. !img 여기서 한가지 의문이 든다. 일반적으로 파일보다 메모리가 성능이 우수한데 왜 카프카가 성능이 좋은 것일까? 그 이유는 카프카의 파일 시스템을 활용한 고성능 디자인에 있다. 일반적으로 하드디스크는 메모리보다 수백배 느리지만 하드디스크의 순차적 읽기에 대한 성능은 메모리보다 크게 떨어지지 않는다고 한다. 컨슈머(Consumer)와 브로커(Broker)에 대해서는 카프카 구성요소에 대한 설명에서 좀 더 자세히 알아보자. 카프카의 구성요소 카프카에는 다음과 같이 여러 구성요소가 있다. topic, partition, offset producer, consumer, consumer group broker, zookeeper replication 구성요소 하나씩 살펴보도록 하자. Topic, Partition : 카프카에 저장되는 메시지는 topic으로 분류되고, topic은 여러개의 patition으로 나눠질수 있다. partition안에는 message의 상대적 위치를 내타내는 offset이 있는데 이 offet정보를 이용해 이전에 가져간 메시지의 위치 정보를 알 수 있고 동시에 들어오는 많은 데이터를 여러개의 파티션에 나누어 저장하기 때문에 병렬로 빠르게 처리할 수 있다. !img Producer, Consumer : 말대로 Producer는 생산(메시지를 Write)하는 주체, Consumer는 소비(메시지를 Read)하는 주체이다. Producer와 Consumer간에는 상호 존재 여부를 알지 못한채 자신에게 주어진 역할만 처리 하게 된다. (위 그림에서 보면 Writes가 Producer) Consumer Group : Producer에서 생산(Write)한 메시지는 여러개의 파티션에 저장을 하는데, 그렇다면 소비하는(Consumer)하는 쪽에서도 여러 소비자가 메시지를 읽어가는것이 훨씬 효율적일 것이다. 하나의 목표를 위해 소비를 하는 그룹, 즉 하나의 토픽을 읽어가기 위한 Counsumer들을 Consumer Group라고 한다. 하지만 이 Consumer Group에는 한가지 룰이 있다. Topic의 파티션은 그 Consumer Group과 1:n 매칭. 즉, 자신이 읽고 있는 파티션에는 같은 그룹내 다른 컨슈머가 읽을 수 없다. (파티션에는 동일한 Consumer Group을 위한 하나의 구멍이 있고, Consumer는 그 구멍에 빨대를 꽂아 읽어간다고 생각하면 쉽게 상상이 될지도….^^;) !img 위 그림과 같이 하나의 토픽에 4개의 파티션이 있고 컨슈머그룹내 3개의 컨슈머가 있다면 컨슈머1,2,3은 각 파티션 1,2,3에 순차적으로 배치가 될 것이고, offset정보를 이용해 순차적으로 데이터를 읽게 된다. 문제는 파티션4인데 컨슈머 갯수가 파티션 갯수보다 작다면 컨슈머 1,2,3중 하나가 파티션4에 접근하여 데이터를 읽게 된다. 만약 파티션 갯수와 컨슈머 갯수가 동일하개 4개씩이라면 각 컨슈머들은 하나의 파티션에서 데이터를 읽게 될것 이고, 파티션갯수가 4개고 컨슈머 갯수가 5개이면 컨슈머5는 그냥 아무일도 안하게 된다.(일반적으로 파티션 갯수와 컨슈머 갯수는 동일하게 구성하는 것을 추천한다고 함.) !img 컨슈머그룹이 존재하는 또 다른 이유가 있다. 물론 이러한 구조로 데이터를 병렬로 읽게 되어 빠른처리가 가능하다는 부분도 있겠지만, 특정 컨슈머에 문제가 생겼을 경우 다른 그룹내 컨슈머가 대신 읽을 수 있게 리벨런싱이 되어 장애 상황에서도 문제 없이 대처할 수 있게 된다. Broker, Zookeeper : broker는 카프카 서버를 칭한다. 동일한 노드내에서 여러개의 broker서버를 띄울 수 있고, Zookeeper는 이러한 분산 메시지큐의 정보를 관리해주는 역할을 한다. 카프카를 띄우기 위해서는 반드시 주키퍼가 실행되어야 한다. Replication : 카프카에서는 replication 수를 임의로 지정하여 topic를 만들 수 있다. replication-factor에 지정하는데 만약 3으로 하면 replication 수가 3이 된다. Kafka Cluster에 3개의 broker가 있고 3개의 Topic이 있다고 가정해보자. Topic-1은 replication-factor 1, Topic-2은 replication-factor 2, Topic-3은 replication-factor 3인 경우이다. !img 그렇다면 replication은 왜 필요할까? 단지 데이터의 복제 용도라기 보다는 특정 borker에 문제가 생겼을 경우 해당 broker의 역할을 다른 broker에서 즉각적으로 대신 수행 할 수 있게 하기 위한 용도 일 것이다. Replication - leader & follower replication을 좀더 자세히 들여다보면, 복제요소중 대표인 leader, 그외 요소인 follower로 나누어진다. topic으로 통하는 모든 데이터의 read/write는 오직 leader에서 이루어지고 follower는 leader와 sync를 유지함으로써 leader에 문제가 생겼을 경우 follower들 중 하나가 leader역할을 하게 되는 것이다. !img 만약 카프카 클러스터내 broker 2에서 장애가 발생되었다면, broker 2에 있던 Topic-2(leader)의 역할을 대신 수행하기 위해 아래 그림과 같이 broker 1에 있는 Topic(follower)가 leader역할을 하게 될 것이다. !img 복제된 데이터가 follower들에게 있으니, 메시지의 유실이 없다는 장점이 있지만, 복제를 하기 위한 시간과 네트워크 비용이 들기 때문에 데이터의 중요도에 따라 ack옵션으로 성능과 데이터의 중요도에 따라 다음과 같이 세부설정이 가능하다. ack (default:1) > - 0 : 프로듀서는 서버로부터 어떠한 ack도 기다리지 않음. 유실율 높으나 높은 처리량 > - 1 : 리더는 데이터를 기록, 모든 팔로워는 확인하지 않음 > - -1(또는 all) : 모든 ISR 확인. 무손실 ack값을 설정하여 데이터의 무손실에 더 중요성을 둘 것인지 또는 유실을 어느정도 감수 하더라고 속도에 중요성을 둘 것인지를 셋팅할 수 있다. 지금까지 설명한 모든 구성요소를 그림으로 표현하면 아래 그림과 같다. !img Producer에서는 메시지를 입력하고, Consumer에서 메시지를 읽어갈때 Zookeeper에서 broker 및 offset정보를 관리하기 때문에 분산처리가 가능하게 된다. 카프카를 운영하기에 앞서 기본적인 구성요소나 매커니즘에 대해 충분히 이해를 하면 운영 하는데 많은 도움이 될 것이다. 참고자료 공식홈 문서 : (http://kafka.apache.org/documentation/)
2019년 01월 18일12분ES6 - Spread Operator
JavascriptSpread Operator!의 기본 전개 연산자 or 펼침 연산자 많이 쓰게 될 연산자이다. > 전개 연산자(spread operator)는 표현식(expression)은 2개 이상의 인수arguments(함수 호출 용)나 2개 이상의 요소elements(배열 리터럴 용) 또는 2개 이상의 변수(비구조화 할당 용)가 예상되는 곳에 확장될 수 있도록 한다. 함수에 인자로 전달 > 을 통해서 apply없이 함수를 호출할 수 있다! 배열 붙이기 >그냥 으로 배열을 껍데기를 깐다고 생각하면 쉽다. 배열 복사하기 >참고해야하는 것은 배열 속 객체는 얕은 복사를 한것이다. 객체의 깊은 복사는 스니핏이 따로 있다. NodeList를 배열로 변환 >를 추출하였을 때, 배열로 바꿔야 할 때가 은근 자주있다. 물론 을 통해 하는 방법도 있지만 보기도 편하고 함수에 직접 때려 박을땐, 이 방법이 가장 깔끔하다.
2018년 06월 17일3분ES6 - Function
Javascript이번은 함수에 대해 알아보자! >1. 보통은 이 두가지를 사용한다. >2. 자가 호출 함수라는 것도 있다. ECMA06에서의 함수 선언 방식 >1. 보면 알겠지만 이젠 더이상 , 을 안써도 된다. >2. 괄호와 중괄호도 생략할 수 있다. 그냥 끝. >3. 매개변수가 없으면 괄호는 써줘야한다. 그냥 다 로만 쓸까? 정답은 NO다. 함수가 뭐하는 함수인지 모를 정도로 짧게 쓸 수 도 있으니 읽기 힘든 코드가 될 수 있다. 거기다가 요녀석이 좀 보기 힘든 구석도 있다. 그래서 선언을 function으로 하는 추세도 있다. 나 같은 함수 내부에서 키워드 대신에 쓰는 것은 실제로 많이 권장하고 있다. >그냥 function 대신에 에로우를 쓰면된다. >매개변수는 먼저쓰고 없으면 써준다.
2018년 05월 07일2분ES6 - for-in for-of의 사용법과 차이점
Javascript진짜 짧게 반복문은 무엇? 기본적인 사용법 For문은 알고 있겠지만 반복적인 작업을 위해 쓰게된다. 이전 포스트에서 for문에서 을 이용한다는 것을 알아 두자. >1. for문은 i가 0부터 arr배열의 길이만큼 3번 반복한다. >2. arr배열의 0번에 접근하여 console.log로 출력한다. 신상 반복문! in과 of for-in loop ES6에서는 2가지 반복문이 추가되었다. 먼저 볼것은 이다. >1. for-in문은 오브젝트에 대해서 반복시킨다. 물론 오브젝트가 아닌 배열에도 사용 할 수 있다. >2. 가끔 JS엔진마다 순서가 바뀔 수 있어서 >3. 받아오는 반복 변수 값은 이다. 그래서 연산작업을 해선 안된다. >를 보면 이해할 수 있다. 그래서? 결론은 . for-of loop for-of에 들어 갈 수 있는 것은 String, Array, NodeList, TypedArray, Map, Set 등이다. >1. 오브젝트는 안된다. >2. 값은 for-in과 다르게 배열 속 값을 그대로 던저준다. >3. 특이한 점은 array 대신에 이 들어갈 수 있다. 사실은 위 예제말고도 상당히 다른 점이 많다. >
2018년 05월 07일3분ES6 - 변수 let, const 사용법
JavascriptECMAScript는 뭐냐? ECMAscript6(ES6)라고도 불리고 ECMA2015라고도 불린다. 자바스크립트의 표준으로 새로운 JS 표준이다. JS같은 경우 함수단위로 스코프가 결정된다든지 호이스팅, 동적 타입이라든지 언어 자체가 느슨한 편이다. JAVA나 C#같은 언어는 엄격한면이 있지만 js너무 느슨하여 혼란스러울때가 종종 생긴다. 변수 변수부터 알아보자 앞서서 스코프 이야기를 했는데, 스코프는 변수 범위다. 원래 JS에서는 로 변수를 선언했다. 보통 를 하지말라는 것을 많이 들어봤을 것이다. >에서 x는 전역변수가 되었다. 거기다가 키워드는 범위가 유연성있다. if속에서 새로 만든 변수같지만 같은 변수로 인식한다. >간단한 예지만 실제로 이런 경우 때문에 원하는 값이 나오지 않아 고생을 많이한다. let, const 가 별로 좋지 않다는 것을 알았다. let과 const는 무슨 차이지? 은 여러번 할당 할 수 있다. 는 한번 뿐이다. >let은 한번 넣고 두번 넣고 할당을 여러번 할 수있지만 const는 그렇지 않다. 당장 어딘가에 let과 const를 사용하고 싶다면? >for-loop문에 var 대신 let을 써보자. for문 밖에서 i값에 접근하지 못한다. >함수를 선언할 때, var대신 const를 써보자 어짜피 함수할당은 1번만 하기 때문에 여러모로 좋다. 배열과 객체에도 를 써도 좋다. >에러가 나야할 것 같지만 로 선언한 배열과 객체 자체를 변경하지 않으면 상관없다.
2018년 05월 06일3분K-최근접 이웃법 (Nearest Neighbor)
Ai최근접 이웃법은 새로운 데이터를 입력받았을 때 가장 가까이 있는 것이 무엇이냐를 중심으로 새로운 데이터의 종류를 정해주는 알고리즘이다. !knn 그림에서 보는것 처럼 기존 데이터가 파랑색과 주황색으로 데이터가 분류되었다고 한다면 물음표 데이터가 들어왔을때 이 데이터는 어떤 색상으로 분류가 되어야 할까? 최근접 이웃법 말그대로 가까운 것에 따른 분류이기 때문에 주황색으로 분류할 것이다. 정말 간단하고 직관적인 알고리즘 이다. 하지만 단순히 가장 가까이에 있는 것으로 분류를 하는것이 문제가 되는 경우도 있다. 다음의 경우를 살펴 보자. !knn 이번에는 문제가 약간 복잡해 진다. 이론대로라면 가장 가까운 것은 파란색이기 때문에 파랑색으로 분류를 해야 할것 같지만, 주변을 보면 대부분 주황색이 보인다. 왠지 파랑색으로 분류하면 안될 것 같다는 생각이 든다.... 단순히 가까운 것에 따른 분류에서 이제 주변에 있는 몇개의 것들 중에 많은 것으로 분류하게 되면 이 문제는 어느정도 해결이 된다. 물음표 주변(큰원)에는 4개의 데이터가 있는데 그중 세개가 주황색, 한개가 파란색 이다. 따라서 물음표는 주황색으로 분류한다. 그렇다면 주변의 범위 즉 주변 데이터의 갯수에 대한 기준이 있어야 할 것 같은데.. 위에서는 4개로 정하였다. 즉 K=4가 되는 최근접 이웃법이다. !knn 만약 K=1로 한다면 처음 정의하였던 것처럼 물음표는 가장 가까운 한개의 요소만 바라보게 될 테니 파랑색으로 분류를 하게 될 것이다. 즉, K의 값에 따라 물음표가 어느 범주로 분류 될 것인지가 결정 된다. 최근접 이웃법 앞에 K라는 단어가 붙어 KNN알고리즘이라고 하는 이유를 이제야 알 것 같다. 그럼 과연 K는 몇이어야 좋은 것일까? 최선의 K값을 선택하는것은 데이터마다 다르게 접근해야 한다. 실습 간단하게 mglearn 라이브러리에 있는 데이터셋을 이용하여 실습해 보자. (mglearn : 그래프와 데이터셋을 손쉽게 다루기 위한 샘플데이터 라이브러리) 결과 !knn 즉 K=3으로 할 경우 테스트 데이터가 어느 분류로 될지를 보여주는 그래프이다. 앞에서 설명했듯이 가장 가까운 3개의 점들에 의해 분류가 결정이 된다. 여기서는 세개의 별이 테스트로 들어온 데이터고 결과적으로는 두개는 주황색, 한개는 파랑색으로 분류가 되었다. 결과 !knn 만약 즉 K=1으로 할 경우 다른 결과를 얻게 된다. mglearn의 make_forge 함수를 이용하여서 data와 target을 x와 y에 대입하여 해당 데이터로 모델을 만들고 검증해보자. 결과 !knn 이 말은 모델이 테스트 데이터셋에 있는 데이터들중 86%를 제대로 맞췄다는 의미이다. 실제 핵심 로직은 sklearn에서 제공해 주고 있기 때문에 코드는 굉장히 간단해 보일 것이다. K-NN알고리즘은 지도학습의 분류 알고리즘의 하나로 로직이 간단하여 구현하기 쉽다. 하지만 학습 모델이 따로 없고, 전체 데이터를 스캔하여 데이터를 분류하기 때문에 데이터의 양이 많아지면 분류 속도가 현저하게 느려진다. 그래서 이라고도 한다.
2018년 02월 14일6분데이터와 알고리즘
Ai!ml-1 머신러닝은 어떤 로 어떤 학습 을 사용할 것인가를 결정하는 작업이라고 할수 있다. 여기서 문제가 될수 있는 나쁜 알고리즘과 나쁜 데이터에 대해 알아보도록 하자. 충분하지 않은 데이터 어린아이에게 사과에 대해 알려주려면 사과를 가리키면서 ‘사과’라고 말하기만 하면 된다(아마도 이 과정을 여러 번 반복해야 하겠지만..). 그러면 아이는 결국 색깔과 모양이 달라도 모든 종류의 사과를 구분할 수 있게 된다. 하지만 머신러닝에서 사과를 구분할 수 있도록 알고리즘이 잘 작동하려면 데이터가 많아야 한다. 아주 간단한 문제에서 조차도 수천 개의 데이터가 필요하고 이미지나 음성 인식 같은 복잡한 문제라면 수백만 개가 필요할지도 모른다. >"It's not who has the best algorithm that wins. It's who has the most data" AI 전문가이자 스탠포드 교수인 앤듀르 응(Andrew Ng)의 말이다. 정교한 알고리즘을 설계하는 것 이상으로 많은 데이터를 이용하는 것이 효과적이다. 정교한 알고리즘과 많은 데이터가 가장 정확한 결과를 얻을 수 있겠지만, 시간과 돈을 알고리즘 개발에 쓰는 것과 데이터를 잘 활용하는것 사이의 트레이드 오프에 대해 다시한번 생각해 봐야 한다는 것이다. !ml-data (참고 논문 : 믿을 수 없는 데이터의 효과 http://goo.gl/KNZMEA) 대표성 없는 학습 데이터 일반화가 잘되려면 우리가 일반화하기 원하는 새로운 사례를 학습 데이터가 잘 대표하는 것이 중요하다. 대표성을 가지는 데이터를 사용하는 것이 가장 좋지만, 결코 쉬운일은 아니다. 샘플이 작으면 샘플링 잡음(Sampling Noise)이 생기고, 표본 추출 방법이 잘못되면 대표성을 띄지 못할 수 있습니다. 이를 샘플링 편향(Sampling Bias)이라고 한다. 샘플링 편향 사례 샘플링 편향에 대한 유명한 사례중 랜던(Randon)과 루즈벨트(Roosevelt)가 경쟁했던 1936년 미국 대통령 선거에서 Literary Digest 잡지사가 천만 명에게 우편물을 보내 수행한 대규모 여론조사가 있다. 그 당시 240만 명의 응답을 받았고 랜던이 선거에서 57% 득표를 얻을 것이라고 높은 신뢰도로 예측했지만, 루즈벨트가 62% 득표로 당선되었다. 문제는 Literary Digest의 샘플링 방법에 있었다고 한다. 첫째, 여론조사용 주소를 얻기 위해 전화번호부, 자사의 구독자 명부, 클럽 회원 명부 등을 사용했다. 이런 명부는 모두 공화당(따라서 랜던)에 투표할 가능성이 높은 부유한 계층에 편중된 경향이 있다. 둘째, 우편물 수신자 중 25% 미만의 사람이 응답했다. 이는 정치에 관심 없는 사람, Literary Digest를 싫어하는 사람과 다른 중요한 그룹을 제외시킴으로써 역시 표본을 편향되게 만들게 된 것이다. 특히 이러한 종류의 샘플링 편향을 비응답 편향(nonresponse bias)이라고 한다. 낮은 품질의 데이터 학습 데이터가 에러, 이상치, 잡음으로 가득하다면 머신러닝 시스템이 내재되어 있는 패턴을 찾기 어려울 수 있다. 그 만큼 데이터의 전처리가 매우 중요하다. 한 설문조사에서는 데이터과학자의 80% 시간을 데이터 수집 및 전처리에 사용한다고 한다. 아마도 전처리 과정은 특히나 지루하고 반복 작업의 연속이라 고단하고 시간도 많이 들어어가게 된다. !clean-data 관련 없는 특성 학습 데이터에 관련 없는 특성이 적고 관련 있는 특성이 충분해야 학습을 진행할 수 있다. 훈련에 사용할 좋은 특성들을 찾아야 하며, 특성을 찾기 위해서는 아래 사항을 고려 해야 한다. 특성 선택 (Feature Selection) : 가지고 있는 특성 중에서 학습에 가장 유용한 특성을 선택. 특성 추출 (Feature Extraction) : 특성을 결합하여 더 유용한 특성을 만듬. 혹은 새로운 데이터를 수집하여 새로운 특성을 만듬. 지금까지 나쁜 데이터의 사례를 살펴보았고 이제 나쁜 알고리즘의 경우를 살펴보자. 학습 데이터의 과대적합 모델이 학습 데이터에 너무 잘 맞지만 일반성이 떨어진다는 의미이다. 훈련 데이터에 있는 잡음의 양에 비해 모델이 너무 복잡할 때 발생하기 때문에 좀더 단순화 시킬 필요가 있다. 파라미터 수가 적은 모델 선택, 훈련데이터에 특성수를 줄임, 모델에 제약을 가하여 단순화 훈련 데이터를 더 많이 모음 훈련 데이터의 잡음을 줄임 학습 데이터 과소적합 모델이 너무 단순해서 데이터의 내재된 구조를 학습하지 못한것을 의미한다. 파라미터가 더 많은 강력한 모델 선택 더 좋은 특성 제공(특성 엔지니어링) 모델의 제약을 줄임(규제 하이퍼파라미터를 감소) 좋은 모델을 만들기 위해서는 테스트와 검증이 필요하다. 이는 일반화 오류를 최소화 하기위한 방법이기도 하다. 일반적으로 모델을 만들기 위해서는 데이터를 훈련셋, 검증셋, 테스트셋으로 나누어 학습을 진행을 한다. !ml-data-set 마치며... "Big Data is the next Natural Resource..." 과거 원유가 가장 중요한 자원이었다고 한다면 이제는 데이터가 가장 중요한 자원일 것이다. 그런데 데이터는 과거의 자원들과는 아주 많이 다르다. 데이터는 지속적인 재생, 고갈되지 않는.. 그리고 스스로 증폭하는 새로운 자원이다. 이렇게 중요한 데이터를 올바르게 잘 활용을 하는것이 정말 중요할 것이다. 참고한 자료 강연 : 4차 산업혁명시대 어떻게 살 것인가? - EBS 미래강연Q 도서 : Hands-On Machine Learning - 한빛미디어
2018년 02월 14일9분