<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Deep Play</title>
    <link>https://3months.tistory.com/</link>
    <description>interested in data analytics and ML modeling</description>
    <language>ko</language>
    <pubDate>Sat, 11 Apr 2026 18:34:13 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Deepplay</managingEditor>
    <image>
      <title>Deep Play</title>
      <url>https://tistory1.daumcdn.net/tistory/1174783/attach/793f1c62e6bc4dc8a61981868c11edb3</url>
      <link>https://3months.tistory.com</link>
    </image>
    <item>
      <title>sparklyr distinct count with condition</title>
      <link>https://3months.tistory.com/636</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;not working&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- date_cnt_mismatch 가 항상 0 이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이유: n_distinct 를 하는 과정에서 NA 가 포함되면 0을 반환함&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745561419652&quot; class=&quot;r&quot; data-ke-language=&quot;r&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user_table &amp;lt;- anal_table %&amp;gt;% group_by(account_id, gender) %&amp;gt;% summarize(
  log_cnt = sum(1),
  log_cnt_mismatch = sum(if_else(ip_check == 1, 1, 0)),
  date_cnt = n_distinct(date_id),
  date_cnt_mismatch = n_distinct(if_else(ip_check == 1, date_id, NA))
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;working&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 간단하게 sql 을 활용해서 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1745561353529&quot; class=&quot;r&quot; data-ke-language=&quot;r&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user_table &amp;lt;- anal_table %&amp;gt;% group_by(account_id, gender) %&amp;gt;% summarize(
  log_cnt = sum(1),
  log_cnt_mismatch = sum(if_else(ip_check == 1, 1, 0)),
  date_cnt = n_distinct(date_id),
  date_cnt_mismatch = sql('count(distinct(if(ip_check == 1, date_id, null)))')
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Tools/R</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/636</guid>
      <comments>https://3months.tistory.com/636#entry636comment</comments>
      <pubDate>Fri, 25 Apr 2025 15:11:41 +0900</pubDate>
    </item>
    <item>
      <title>Openai API 기본적인 호출방식</title>
      <link>https://3months.tistory.com/635</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RAG 를 사용하지 않은 기본적인 API Call 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 기본 Openai API&lt;/h4&gt;
&lt;pre id=&quot;code_1739860029622&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from openai import OpenAI
import os
os.environ[&quot;OPENAI_API_KEY&quot;] = &quot;sk...&quot;

client = OpenAI()
completion = client.chat.completions.create(
    model=&quot;gpt-4o&quot;,
    temperature=0.1,
    messages=[
        {&quot;role&quot;:&quot;system&quot;, &quot;content&quot;:system_prompt},
        {&quot;role&quot;:&quot;user&quot;, &quot;content&quot;:user_query}
    ],
    response_format={&quot;type&quot;:&quot;json_object&quot;}
)
response = completion.choices[0].message.content
return(response)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Llama_index 활용&lt;/h4&gt;
&lt;pre id=&quot;code_1739859813907&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from llama_index.llms.openai import OpenAI
from llama_index.core.llms import ChatMessage
import os
os.environ[&quot;OPENAI_API_KEY&quot;] = &quot;sk...&quot;

# OpenAI 모델 설정
llm = OpenAI(model=&quot;gpt-4o&quot;, temperature=0.1)

messages = [
    ChatMessage(role=&quot;system&quot;, content=system_prompt),
    ChatMessage(role=&quot;user&quot;, content=&quot;Hi&quot;),
]

# Response 를 json 형식으로 받고 싶은 경우 
resp = llm.chat(messages, response_format={&quot;type&quot;: &quot;json_object&quot;})&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Data science/AI</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/635</guid>
      <comments>https://3months.tistory.com/635#entry635comment</comments>
      <pubDate>Tue, 18 Feb 2025 15:27:37 +0900</pubDate>
    </item>
    <item>
      <title>RAG Framework 무엇을 사용해야할까? LangChain vs. LlamaIndex</title>
      <link>https://3months.tistory.com/634</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RAG Framework&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG (Retrieval Augmented Generation) 은 LLM 이 정보를 검색하고 이를 활용하여 결과 생성할 수 있도록 하는 방법이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GJ5Ss/btsMk0MXMpP/ueUDobX9NhUF7zVv9xNt91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GJ5Ss/btsMk0MXMpP/ueUDobX9NhUF7zVv9xNt91/img.png&quot; data-alt=&quot;RAG 의 4가지 컴포넌트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GJ5Ss/btsMk0MXMpP/ueUDobX9NhUF7zVv9xNt91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGJ5Ss%2FbtsMk0MXMpP%2FueUDobX9NhUF7zVv9xNt91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;381&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RAG 의 4가지 컴포넌트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유명한 프레임워크로 LangChain 과 LlamaIndex&lt;/b&gt; 가 있다. 이 포스트에서는 두 프레임워크에 어떤 차이점이 있는지를 살펴보려고 한다. &lt;b&gt;RAG 에는 4가지 컴포넌트가 있다. Loaders, Splitters, Indexing, Chains 이 그것&lt;/b&gt;이다. 각각 LangChain과 LlamaIndex 가 이를 어떻게 구현하고 있는지 살펴보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Loaders&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Loader 는 API, document, DB 와 같은 다양한 소스들을 로드하는 객체이다. LangChain, LlamaIndex 모두 흔하게 사용되는 소스들을 로드할 수 있는 built-in 함수들을 제공한다. 로더를 통해 파일 위치를 입력해주면 Directory 객체가 반환되며, 이를 다시 한 번 더 로드해주면 텍스트를 로드할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain: Text 파일 로드하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739767458802&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain.document_loaders import TextLoader
# Load a text document
loader = TextLoader(&quot;sample.txt&quot;)
documents = loader.load()
print(documents[0].page_content)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LlamaIndex: 특정 디렉토리안에 있는 텍스트 파일 로드하기&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1739767478752&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from llama_index.core import SimpleDirectoryReader
# Load a text document from a directory
loader = SimpleDirectoryReader('path/to/docs')
documents = loader.load_data()
print(documents[0].text)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Splitters&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Splitter 는 도큐먼트를 작은 Chunk 단위로 분해해서 GPT 나 BERT 의 &lt;b&gt;token limit 을 넘기지 않도록 만들어주는 역할&lt;/b&gt;을 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain 의 `TextSplitter` 는 텍스트를 Character, Word, Sentence 중 어떤 단위로 분할할지를 선택&lt;/b&gt;할 수 있다. 아래&amp;nbsp;코드는 Character 단위로 텍스트를 분할하며, chunk size 는 1000이고, chunk 간에 200개의 중복 두는 방식으로 텍스트를 분할한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739768568501&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain.text_splitter import CharacterTextSplitter

# Define a character splitter
splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(documents)
print(chunks[0].page_content)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LlamaIndex&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LlamaIndex &lt;/b&gt;에서는 아래와 같이 똑같은 작업을 수행할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739768837505&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from llama_index import TokenTextSplitter

# Define a token splitter
splitter = TokenTextSplitter(chunk_size=1000)
chunks = splitter.split(documents)
print(chunks[0].text)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Indexing&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Indexing 은 RAG system 에서 가장 핵심 부분이라고 할 수 있다. &lt;b&gt;유저 쿼리와 가장 관련있는 청크를 빠르고 효율적으로 검색&lt;/b&gt;할 수 있어야한다. &lt;b&gt;VectorStoreIndex&lt;/b&gt; 라는 말이 자주 등장하는데, 이는 &lt;b&gt;VectorStore 를 빠르게 검색해서 유저 쿼리와 연관된 청크를 반환해주는 색인 시스템&lt;/b&gt;을 의미한다고 이해하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain 의 VectorStoreIndex 는 Splitter 를 통해 만들어진 청크들을 통해 인덱스를 생성한다. 그리고 Similarity search 를 기반으로 동작하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1739769047975&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain.vectorstores import FAISS
from langchain.embeddings.openai import OpenAIEmbeddings

# Create embeddings
embedding_model = OpenAIEmbeddings()

# Create FAISS index
index = FAISS.from_documents(chunks, embedding_model)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LlamaIndex&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LlamaIndex 에서는&amp;nbsp; 아래와 같이 &lt;b&gt;Document 로부터 바로 VectorStoreIndex 를 생성&lt;/b&gt;할 수 있고, Splitter 를 바로 인자로 주어 생성할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739771715549&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from llama_index.core import VectorStoreIndex
embed_model = OpenAIEmbedding(model=&quot;text-embedding-3-small&quot;)

index = VectorStoreIndex.from_documents(
    documents,
    embed_model=embed_model,
    transformations=[SentenceSplitter(chunk_size=16000)]
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. Chain&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 에서 Chain 이란 Retrieval 과 Generation 의 요소들이 결합된 일련의 작업들이라고 보면 된다. LangChain 과 LlamaIndex 모두 이러한 RAG 의 컴포넌트를 엮어서 하나의 체인으로 만드는 기능을 제공한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain 에서는 아래와 같이 `RetrievalQA` 를 활용해서 LLM 과 Retrieval 가 결합된 체인을 만들 수 있다. 이를 통해 RAG workflow 를 좀 더 간결하게 표현할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739772558542&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain.chains import RetrievalQA
from langchain.llms import OpenAI

# Combine FAISS index and OpenAI for RAG
llm = OpenAI()
qa_chain = RetrievalQA(llm=llm, retriever=index.as_retriever())
result = qa_chain.run(&quot;What is the content of the document?&quot;)
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LlamaIndex&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LlamaIndex 에서는 VectorStoreIndex 에 as_query_engine 또는 as_chat_engine 을 이용해서 쉽게 Index 와 LLM 을 연결할 수 있다. 해당 Index 를 활용하는 LLM 을 만들라는 의미이다.&amp;nbsp; &lt;b&gt;query engine 과 chat engine 의 차이점은 chat engine 은 컨텍스트를 기억한다&lt;/b&gt;. as_chat_engine 을 보면, &lt;b&gt;memory 인자를 전달함으로써, 과거 기억을 얼마나 전달할지를 정할 수 있다.&lt;/b&gt; token_limit 이 작을수록 과거 컨텍스트를 모델에 더 적게 전달한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739772658264&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Chat Engine 생성하기
memory = ChatMemoryBuffer.from_defaults(token_limit=16000)
chat_engine = index.as_chat_engine(
    chat_mode=&quot;context&quot;,
    memory=memory,
    system_prompt=system_prompt
)
response = chat_engine.query(&quot;What is the content of the document?&quot;)
print(response)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;LangChain 과 LlamaIndex 중 무엇을 선택해야할까?&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Customization&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain&lt;/b&gt; 의 장점은 &lt;b&gt;다양한 소스를 기반으로 RAG 를 구현해야할 때 더욱 유연&lt;/b&gt;하다는 것이다. 예를 들어, Multi-modal RAG 시스템을 구축한다고 해보자. PDF, API 문서, Web 문서 등을 각각 Load 해서 VectorStoreIndex 를 구축한다고 해보자. LangChain 에서는 `+` operation 을 통해 쉽게 로더를 결합할 수 있다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739773529859&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain.chains import SequentialChain
from langchain.llms import OpenAI
from langchain.document_loaders import WebPageLoader, TextLoader
from langchain.vectorstores import FAISS

# Step 1: Load data from multiple sources
pdf_loader = TextLoader(&quot;docs/sample.pdf&quot;)
web_loader = WebPageLoader(url=&quot;https://example.com&quot;)
documents = pdf_loader.load() + web_loader.load()

# Step 2: Create embeddings and index
embedding_model = OpenAIEmbeddings()
index = FAISS.from_documents(documents, embedding_model)

# Step 3: Build a chain that retrieves and generates responses
llm = OpenAI()
qa_chain = RetrievalQA(llm=llm, retriever=index.as_retriever())

# Run the chain
response = qa_chain.run(&quot;What are the key points in the PDF and website?&quot;)
print(response)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LlamaIndex&lt;/b&gt; 의 장점은 더욱 직관적이며, 심플하게 RAG 를 구현할 수 있다는 것이다. 예를 들어 LlamaIndex 의 경우, 법 문서나 medical report 등을 기반으로 RAG 를 만들 때, 매우 빠르게 최소한의 코드로 구축해볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739774356874&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from llama_index.core import VectorStoreIndex, SimpleDirectoryReader

# Load documents from a directory
loader = SimpleDirectoryReader('docs/')
documents = loader.load_data()

# Build a tree index and query
index = VectorStoreIndex(documents)
query_engine = index.as_query_engine()

# Query the system
response = query_engine.query(&quot;Summarize the legal document.&quot;)
print(response)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Ecosystem 통합&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain&lt;/b&gt; 은 여러 AI Ecosystem 들을 통합할 때 더욱 유용하다. LangChain 은&lt;b&gt; Multi-LLM, Multi-Retrieval 를 지원한다고 볼 수 있다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Vector Database : FAISS, Pinecone, Chroma&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Language Model : OpenAI, GPT-4, Anthropic's Claude&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- API : Hugging Face, Cohere&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위와 같은 요소들을 자유롭게 결합할 수 있다는 것이 LangChain 의 장점&lt;/b&gt;이라고 볼 수 있다. 예를 들어, OpenAI 의 GPT-4 와 HuggingFace 의 BERT 를 태스크에 따라 자유롭게 스위치 할 수 있다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739779764575&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain.chains import LLMChain
from langchain.chains import SimpleSequentialChain
from langchain.llms import OpenAI, HuggingFaceHub
from langchain_core.prompts import PromptTemplate

prompt_template = &quot;Tell me a {adjective} joke&quot;
prompt = PromptTemplate(
    input_variables=[&quot;adjective&quot;], template=prompt_template
)

gpt_chain = LLMChain(llm=OpenAI(), prompt=prompt)
gpt_chain.invoke(&quot;scary&quot;)

bert_chain = LLMChain(llm=HuggingFaceHub(model=&quot;bert-large-uncased&quot;), prompt=prompt)
bert_chain.invoke(&quot;scary&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 LlamaIndex 의 경우 LangChain 과 같은 유연성은 떨어지지만, 앞서 언급한 것처럼 빠르게 document 기반의 RAG 를 만들 때 유용하다고 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Multi-retrieval&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain&lt;/b&gt; 의 경우&lt;b&gt; Multi-retrieval &lt;/b&gt;를 구현할 수 있다. 예를 들어, 유저 쿼리를 받아, 법과 관련된 쿼리라면, 법 문서에 적용된 key-based retrieval 를 호출하고, 과학 관련 쿼리라면, 과학&amp;nbsp;논문에 적용된 embedding-based retrieval 를 호출하는 multi-retrieval 를 구현해보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739782380953&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain.retrievers.multi_retriever import MultiRetriever
from langchain.retrievers import FAISSRetriever, KeywordRetriever

# Define keyword and embedding-based retrieval systems
keyword_retriever = KeywordRetriever(documents=legal_documents)
embedding_retriever = FAISSRetriever(index=scientific_index)

# Combine them in a MultiRetriever
retriever = MultiRetriever(retrievers={
    'legal': keyword_retriever,
    'science': embedding_retriever
})

# Query the retriever with a legal question
response = retriever.retrieve(&quot;What are the recent changes in contract law?&quot;)
print(response)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. Community Support 측면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain&lt;/b&gt; 은 위와 같은 advanced 된 기능들 때문에 최근 빠르게 관련 커뮤니티가 성장하고 있으며&lt;b&gt;, Llama Index 와 비교하여 좀 더 성장된 ecosystem 을 갖고 있다.&lt;/b&gt; &lt;b&gt;LlamaIndex&lt;/b&gt; 의 경우 비교적 최근에 등장했지만 마찬가지로 빠르게 관심을 얻고 있으며, 앞서 언급한 것처럼 &lt;b&gt;좀 더 간단한 작업에 적합&lt;/b&gt;하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결로: 결론적으로 어떤 상황에서 어떤 프레임워크를 선택해야할까?&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;LangChain 을 선택하면 좋은 상황&lt;/b&gt;&lt;br /&gt;- 다양한 구성 요소들을 자유롭게 커스텀하고 싶을 때 (Retrieval, LLM, OpenAI, HuggingFace, FAISS, Chroma...)&lt;br /&gt;- VectorStore 를 다양한 타입의 소스로 구성하고 싶을 때 (Text, PDF, API 등)&lt;br /&gt;- 다양한 Retrieval 전략을 사용하고 싶을 때 (Keyworkd search, post-retrieval ranking 등)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;LlamaIndex 를 선택하면 좋은 상황&amp;nbsp;&lt;/b&gt;&lt;br /&gt;- 간단한 RAG System 을 구축하고 싶을 때 (예를 들어, document 를 요약해주는 AI 를 구현)&lt;br /&gt;- 특별한 커스텀이 필요없을 때&amp;nbsp;&lt;br /&gt;- 구조적인 문서 (법 관련 문서 등) 을 기반으로 RAG 를 구축할 때 잘 작동함&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://medium.com/@tam.tamanna18/langchain-vs-llamaindex-a-comprehensive-comparison-for-retrieval-augmented-generation-rag-0adc119363fe&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://medium.com/@tam.tamanna18/langchain-vs-llamaindex-a-comprehensive-comparison-for-retrieval-augmented-generation-rag-0adc119363fe&lt;/a&gt;&lt;/p&gt;</description>
      <category>Data science/AI</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/634</guid>
      <comments>https://3months.tistory.com/634#entry634comment</comments>
      <pubDate>Mon, 17 Feb 2025 13:48:52 +0900</pubDate>
    </item>
    <item>
      <title>chroma db 설치시 sqlite3 에러 해결</title>
      <link>https://3months.tistory.com/633</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 가상환경을 새롭게 생성하고 거기에 설치하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가상환경 생성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1738909863389&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;virtualenv openai
python3 -m ipykernel install --user --name=openai

# 가상환경 activate
source ./activate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;pysqlite3 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1738909888772&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install pysqlite3-binary

# 내장된 pysqlite3 대신 pysqlite3 을 사용하도록 설정한다. 
import sys
import pysqlite3
sys.modules[&quot;sqlite3&quot;] = pysqlite3

import sqlite3
print(sqlite3.sqlite_version)
&amp;gt;&amp;gt; 3.46.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.35 이상 버전에서 chromadb 를 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1738909931557&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import chromadb&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Data science/AI</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/633</guid>
      <comments>https://3months.tistory.com/633#entry633comment</comments>
      <pubDate>Fri, 7 Feb 2025 15:32:17 +0900</pubDate>
    </item>
    <item>
      <title>기본적인 RAG 를 좀 더 개선해보기 (w/ langchain)</title>
      <link>https://3months.tistory.com/631</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기본적인&amp;nbsp;RAG&amp;nbsp;를&amp;nbsp;좀&amp;nbsp;더&amp;nbsp;개선해보기&amp;nbsp;(w/&amp;nbsp;langchain)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 RAG 의 개념 : &lt;b&gt;기본 RAG (Retrieval Augmented Generation)&lt;/b&gt; 는 query 를 입력으로 받아 vector store 로부터 relevant 한 chunk 를 검색해서 가져온 후, 이를 prompt 에 추가해서 최종적인 답변을 출력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vgfeO/btsKouh9Sxk/TKNrl29y2T6GYWDbxmukRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vgfeO/btsKouh9Sxk/TKNrl29y2T6GYWDbxmukRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vgfeO/btsKouh9Sxk/TKNrl29y2T6GYWDbxmukRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvgfeO%2FbtsKouh9Sxk%2FTKNrl29y2T6GYWDbxmukRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;192&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;796&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용된 개념&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Relevance Check : vector store 에서 retrive 해온 chunk 가 relevant 한지 LLM 을 활용해서 검증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Hallucination Check&amp;nbsp; : retrieved chunk 를 참고해서 생성한 최종 답변이 hallucination 인지 여부를 LLM 을 활용해서 검증한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 를 사용하기 위한 프레임워크로 langchain 을 사용한다. (비슷한 역할을 하는 Llamaindex 라는 프레임워크도 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;목적:&lt;/b&gt; 3가지 웹문서를 vector store 로 저장하고, 이에 기반해 사용자  질문에 답변하는 RAG 를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 필요한 라이브러리 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730251862397&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;%pip install langchain langchain-openai langchain-openai langchain_chroma langchain-text-splitters langchain_community&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. LLM 객체 생성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730251997850&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import getpass
import os

os.environ[&quot;OPENAI_API_KEY&quot;] = &quot;API_KEY 를 입력하세요.&quot;
from langchain_openai import ChatOpenAI

# openai의 gpt-4o-mini 모델 사용
llm = ChatOpenAI(model=&quot;gpt-4o-mini&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 문서를 로드한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- langchain 의 WebBaseLoader를 활용하며, beautiful soup 라이브러리를 활용해 html 태그 등을 parsing 해 본문 내용만 가져온다고 이해하면된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730252228725&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import bs4
from langchain import hub
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter

urls = [
    &quot;https://lilianweng.github.io/posts/2023-06-23-agent/&quot;,
    &quot;https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/&quot;,
    &quot;https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/&quot;,
]

# Load, chunk and index the contents of the blog.
loader = WebBaseLoader(
    web_paths=urls,
    bs_kwargs=dict(
        parse_only=bs4.SoupStrainer(
            class_=(&quot;post-content&quot;, &quot;post-title&quot;, &quot;post-header&quot;)
        )
    ),
)
docs = loader.load()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 로드한 문서를 Split 해서 Vector store 로 저장한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- chunk size 는 1000&amp;nbsp; 토큰이며, 200의 오버랩을 허용한다. 각 청크간에 약간의 중복을 준다는 의미이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- chunk 를 vector 로 저장할 때, embedding 이 필요한데, openai 의 text-embedding-3-small 모델을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- retrieval 의 세팅으로 similarity 를 기준으로 6개의 chunk 를 가져오는 retrieval 객체를 정의한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730252649809&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings(model=&quot;text-embedding-3-small&quot;))
# Retrieve and generate using the relevant snippets of the blog.
retriever = vectorstore.as_retriever(search_type=&quot;similarity&quot;, search_kwargs={'k':6})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. user query 를 입력으로 받아 관련도가 높은 chunk 를 retrieve 해온다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 가져온 chunk 6개를 1번 chunk : ... 2번 chunk : ... 이런 형식으로 텍스트 형식으로 저장한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730252827715&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
from langchain import hub
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

prompt = hub.pull(&quot;rlm/rag-prompt&quot;)

user_query = &quot;What is agent memory?&quot;
retrieved_docs = retriever.invoke(&quot;what is agent memory?&quot;)
retrieved_docs_text = &quot;&quot;
for i in range(0,6) :
    doc_content = retrieved_docs[i].page_content
    retrieved_docs_text += f'{i+1}번 chunk: {doc_content}\n'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6. 목적에 맞는 최종 prompt 를 작성한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- relevance check 와 hallucination check 를 하여 기본 RAG 를 좀 더 개선하는 것이 목적이므로, 이에 대한 로직을 추가한 프롬프트를 작성한다. 전체적인 답변 생성 로직과 함께 필요한 정보 - 유저 query 와 retrieved chunk 를 제공해주어야한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730252993774&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;query = {&quot;question&quot;: {f&quot;&quot;&quot;
     너는 유저로부터 query 를 입력 받아서 최종적인 답변을 해주는 시스템이야.

     그런데, retrieval 된 결과를 본 후에, relevance 가 있는 retrieved chunk 만을 참고해서 최종적인 답변을 해줄거야.
     아래 step 대로 수행한 후에 최종적인 답변을 출력해.

     Step1) retrieved chunk 를 보고 관련이 있으면 각각의 청크 번호별로 relevance: yes 관련이 없으면 relevance: no 의 형식으로 json 방식으로 출력해.\n
     Step2) 만약 모든 chunk 가 relevance 가 no 인 경우에, relevant chunk 를 찾을 수 없었습니다. 라는 메시지와 함께 프로그램을 종료해.
     Step3) 만약 하나 이상의 chunk 가 relevance 가 yes 인 경우에, 그 chunk들만 참고해서 답변을 생성해.
     Step4) 최종 답변에 hallucination 이 있었는지를 평가하고 만약, 있었다면 hallucination : yes 라고 출력해. 없다면 hallucination : no 라고 출력해.
     Step5) 만약 hallucination : no 인 경우에, Step3로 돌아가서 답변을 다시 생성해. 이 과정은 딱 1번만 실행해 무한루프가 돌지 않도록.
     Step6) 지금까지의 정보를 종합해서 최종적인 답변을 생성해

     답변은 각 스텝별로 상세하게 출력해

     아래는 user query 와 retrieved chunk 에 대한 정보야.

     query : {user_query}\n
     retrieved chunks: {retrieved_docs_text}&quot;&quot;&quot;}}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7. Prompt Template 을 기반으로 RAG Chain 을 호출해 최종 답변을 json 형식으로 출력한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- prompt template 은 프롬프트를 구조화된 형식으로 작성할 수 있게 도와주는 클래스이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- rag chain 은 query-&amp;gt; prompt -&amp;gt; llm -&amp;gt; output 파이프라인을 하나의 함수로 실행할 수 있도록 해준다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730253152349&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;parser = JsonOutputParser()

prompt = PromptTemplate(
    template=&quot;Answer the user query.\n{format_instructions}\n{query}\n&quot;,
    input_variables=[&quot;query&quot;],
    partial_variables={&quot;format_instructions&quot;: parser.get_format_instructions()},
)

rag_chain = (
    prompt
    | llm
    | StrOutputParser()
)

rag_chain.invoke({&quot;query&quot;: query})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;최종적으로 생성된 답변&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;json\n{\n&amp;nbsp;&amp;nbsp;&quot;step1&quot;: {\n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;1&quot;: &quot;relevance: yes&quot;,\n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;2&quot;: &quot;relevance: yes&quot;,\n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;3&quot;: &quot;relevance: yes&quot;,\n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;4&quot;: &quot;relevance: yes&quot;,\n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;5&quot;: &quot;relevance: no&quot;,\n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;6&quot;: &quot;relevance: no&quot;\n&amp;nbsp;&amp;nbsp;},\n&amp;nbsp;&amp;nbsp;&quot;step2&quot;: &quot;All retrieved chunks have relevance.&quot;,\n&amp;nbsp;&amp;nbsp;&quot;step3&quot;: &quot;The relevant chunks provide information about agent memory, which consists of both long-term and short-term memory. Agent memory is a long-term memory module that records agents\' experiences in natural language. Observations can trigger new statements, and the retrieval model assists in informing the agent&amp;rsquo;s behavior based on relevance, recency, and importance. Additionally, the reflection mechanism synthesizes these memories into higher-level inferences over time.&quot;,\n&amp;nbsp;&amp;nbsp;&quot;step4&quot;: &quot;hallucination: no&quot;,\n&amp;nbsp;&amp;nbsp;&quot;step5&quot;: &quot;Since hallucination is no, I will generate the answer again.&quot;,\n&amp;nbsp;&amp;nbsp;&quot;step6&quot;: {\n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;final_answer&quot;: &quot;Agent memory refers to a long-term memory module that captures a comprehensive list of an agent\'s experiences in natural language, which can be used to inform the agent\'s behavior. It includes short-term aspects, such as in-context learning, as well as long-term memory that retains information over extended periods. The retrieval model helps prioritize memories based on relevance, recency, and importance, while the reflection mechanism synthesizes past events into higher-level insights that guide future behavior.&quot;\n&amp;nbsp;&amp;nbsp;}\n}\n&lt;/p&gt;</description>
      <category>Data science/AI</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/631</guid>
      <comments>https://3months.tistory.com/631#entry631comment</comments>
      <pubDate>Wed, 30 Oct 2024 11:22:45 +0900</pubDate>
    </item>
    <item>
      <title>Hive 에서 percentile_approx 가 느린 이유와 해결 방법</title>
      <link>https://3months.tistory.com/628</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 일별로 유저수와 그 유저의 피쳐 a,b,c 에 대해 각각 평균과 중위수를 구하는 코드이다. 이 코드는 상당히 시간이 오래 걸린다.&lt;/p&gt;
&lt;pre id=&quot;code_1727416416150&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select 
date_id,
count(distinct  user_id) as user_cnt,
avg(a) as avg_a,
avg(b) as avg_b,
avg(c) as avg_c,
percentile_approx(a, 0.5) as med_a,
percentile_approx(b, 0.5) as med_b,
percentile_approx(c, 0.5) as med_b
from table
group by date_id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, user_cnt 와 평균을 구하는 코드를 제외한 아래 쿼리는 빠르게 실행된다. 왜 그럴까?&lt;/p&gt;
&lt;pre id=&quot;code_1727416439893&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select 
date_id,
percentile_approx(a, 0.5) as med_a,
percentile_approx(b, 0.5) as med_b,
percentile_approx(c, 0.5) as med_b
from table
group by date_id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;챗 GPT 를 활용해 답변을 생성해보니 아래와 같다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;유저수를&amp;nbsp;count하는&amp;nbsp;연산과&amp;nbsp;평균을&amp;nbsp;구하는&amp;nbsp;연산이&amp;nbsp;맵리듀스&amp;nbsp;분산&amp;nbsp;처리시(Big&amp;nbsp;Data&amp;nbsp;환경을&amp;nbsp;가정)&amp;nbsp;각&amp;nbsp;노드에서&amp;nbsp;개별적으로&amp;nbsp;이루어져서&amp;nbsp;빠른&amp;nbsp;처리가&amp;nbsp;가능하지만,&amp;nbsp;중앙값을&amp;nbsp;구하는&amp;nbsp;&lt;b&gt;percentile_approx&amp;nbsp;연산은&amp;nbsp;전체&amp;nbsp;데이터를&amp;nbsp;한&amp;nbsp;노드에서&amp;nbsp;모아서&amp;nbsp;처리해야&amp;nbsp;하기&amp;nbsp;때문에&amp;nbsp;상대적으로&amp;nbsp;시간이&amp;nbsp;많이&amp;nbsp;소요&lt;/b&gt;됩니다.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;하지만,&amp;nbsp;여러&amp;nbsp;컬럼의&amp;nbsp;중앙값을&amp;nbsp;동시에&amp;nbsp;구하는&amp;nbsp;쿼리에서는&amp;nbsp;중앙값을&amp;nbsp;구하는&amp;nbsp;작업이&amp;nbsp;모든&amp;nbsp;컬럼에&amp;nbsp;대해&amp;nbsp;동시에&amp;nbsp;이루어지기&amp;nbsp;때문에&amp;nbsp;시간이&amp;nbsp;대폭&amp;nbsp;줄어듭니다.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;즉,&amp;nbsp;count와&amp;nbsp;avg는&amp;nbsp;분산처리가&amp;nbsp;가능하여&amp;nbsp;각각의&amp;nbsp;연산이&amp;nbsp;독립적이지만&amp;nbsp;percentile_approx는&amp;nbsp;분산처리가&amp;nbsp;어려워서&amp;nbsp;전체&amp;nbsp;데이터에&amp;nbsp;대한&amp;nbsp;연산이&amp;nbsp;필요하여&amp;nbsp;시간이&amp;nbsp;오래&amp;nbsp;걸린&amp;nbsp;것입니다.&amp;nbsp;두번째&amp;nbsp;쿼리는&amp;nbsp;&lt;b&gt;percentile_approx만&amp;nbsp;있기&amp;nbsp;때문에&amp;nbsp;동시에&amp;nbsp;처리될&amp;nbsp;수&amp;nbsp;있어&amp;nbsp;속도가&amp;nbsp;빠른&amp;nbsp;것&lt;/b&gt;입니다.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;확인하기&amp;nbsp;위해,&amp;nbsp;각각의&amp;nbsp;연산을&amp;nbsp;따로&amp;nbsp;실행하는&amp;nbsp;쿼리를&amp;nbsp;만들어&amp;nbsp;시간을&amp;nbsp;체크해보면&amp;nbsp;더&amp;nbsp;정확한&amp;nbsp;비교가&amp;nbsp;가능할&amp;nbsp;것입니다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- hive sql 에서 &lt;b&gt;percentile_approx 는 분산처리가 어렵고 전체 데이터에 대한 연산이 필요하기 때문에 시간이 오래 걸린다.&amp;nbsp;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- hive sql 에서 percentile_approx 를 활용해 중위수를 구할 때에는 다른 집계는 하지 말고 중위수만 구하자. 그럼 조금 더 빠르다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;</description>
      <category>Tools/SQL</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/628</guid>
      <comments>https://3months.tistory.com/628#entry628comment</comments>
      <pubDate>Fri, 27 Sep 2024 14:54:40 +0900</pubDate>
    </item>
    <item>
      <title>R studio server 에서 knitr 에러 발생시 해결 방법</title>
      <link>https://3months.tistory.com/627</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;R&amp;nbsp;studio&amp;nbsp;server&amp;nbsp;에서&amp;nbsp;knitr&amp;nbsp;에러&amp;nbsp;발생시&amp;nbsp;해결&amp;nbsp;방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 오류 또는 test_env 를 찾을 수 없습니다. 등, Rmd 파일을 knitr 을 통해 변환시킬 때 에러가 발생했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;에러 메시지 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1725352361994&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  |................................                                 |  50%
  ordinary text without R code

  |.................................................................| 100%


processing file: Preview-b0c112a265.Rmd
label: unnamed-chunk-1

Quitting from lines 16-26 (Preview-b0c112a265.Rmd) 
Error in file(file, &quot;rt&quot;) : cannot open the connection
Calls: &amp;lt;Anonymous&amp;gt; ... withVisible -&amp;gt; eval -&amp;gt; eval -&amp;gt; read.csv -&amp;gt; read.table -&amp;gt; file
Execution halted&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결:&lt;/b&gt; [Tool] - [Global Options] - [R Markdown] - Evaluate Chunks in Directory 를 current 로 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 세팅을 변경하자 문제가 해결되었다. 위 문제로 인해 마크다운 문서를 작성할 때 Rstudio server 가 아니라 로컬에서 작업을 항상 했었는데 불편함이 존재했었다. 위 방법으로 깔끔하게 해결되었고, 앞으로 마크다운 문서 작성도 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;R studio server 에서 하면 될듯하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 : &lt;a href=&quot;https://stackoverflow.com/questions/26994958/error-cannot-open-the-connection-in-executing-knit-html-in-rstudio&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://stackoverflow.com/questions/26994958/error-cannot-open-the-connection-in-executing-knit-html-in-rstudio&lt;/a&gt;&lt;/p&gt;</description>
      <category>Tools/R</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/627</guid>
      <comments>https://3months.tistory.com/627#entry627comment</comments>
      <pubDate>Tue, 3 Sep 2024 17:35:57 +0900</pubDate>
    </item>
    <item>
      <title>R - Distill + github pages 로 간단한 블로그 만들기</title>
      <link>https://3months.tistory.com/625</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;R&amp;nbsp;-&amp;nbsp;Distill&amp;nbsp;+&amp;nbsp;github&amp;nbsp;pages&amp;nbsp;로&amp;nbsp;간단한&amp;nbsp;블로그&amp;nbsp;만들기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R markdown 형식을 html 로 변환해 호스팅하는 패키지로 blogdown 이 유명했는데 최근 많은 유저들이 distill 로 많이 갈아타고 있는듯하다. blogdown 의 경우 hugo 를 기본으로 블로그 템플릿이 만들어지는데 hugo 보다 distill 의 기본 포맷이 조금 더 학술적인 느낌이기 때문에 개인적으로 distill 이 R 관련 포스팅에 좀 더 적합한 느낌으로 보인다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Distill Manual :&lt;/b&gt; &lt;a href=&quot;https://rstudio.github.io/distill/blog.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://rstudio.github.io/distill/blog.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;The Distillery:&lt;/b&gt; Distill 매뉴얼은 조금 드라이하게 작성 되어 있는듯하다. distill 사용을 좀 더 잘하기 위한 tip 등을 공유하는 사이트로 The Distillery 가 있다. (&lt;a href=&quot;https://distillery.rbind.io/tips_and_tricks&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://distillery.rbind.io/tips_and_tricks&lt;/a&gt;) 여기에서는 Distill 을 사용할 때의 &lt;a href=&quot;https://distillery.rbind.io/tips_and_tricks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;tips and tricks&lt;/a&gt; 와 &lt;a href=&quot;https://distillery.rbind.io/showcase&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;showcase&lt;/a&gt; 를 공유한다. showcase 를 보면 다른 유저들이 distill 을 이용해서 블로그를 어떻게 운영하고 있는지를 볼 수 있어 개인 블로그를 만드는데 참고가 될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 Distill 과 Github pages 를 통해 10분 이내로 블로그를 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 github 계정, git 설치, R 설치는 기본적으로 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Distill 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1724414864631&quot; class=&quot;r&quot; data-ke-language=&quot;r&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;install.packages('distill')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Distill Blog Project 생성&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;create-blog.png&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YRNbp/btsJfh4kl7b/bNLeKRiJk3WuywiVYsQb91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YRNbp/btsJfh4kl7b/bNLeKRiJk3WuywiVYsQb91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YRNbp/btsJfh4kl7b/bNLeKRiJk3WuywiVYsQb91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYRNbp%2FbtsJfh4kl7b%2FbNLeKRiJk3WuywiVYsQb91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1062&quot; height=&quot;756&quot; data-filename=&quot;create-blog.png&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 블로그를 생성하면 홈폴더 바로 아래에 있는 하위 폴더에 &quot;_site&quot; 라는 폴더가 있는데 여기에 웹사이트 최초 시작지점인 index.html 파일이 존재한다. 이렇게 github pages 로 push 하면 [githubID].github.io/_site 가 블로그의 주소가 된다. 만약 [githubId].github.io 를 주소로 블로그를 호스팅하고 싶다면, _site 에 위치한 파일들을 모두 home 폴더로 옮겨준다. 그리고 홈폴더 바로 아래에 있는 _site.yaml 파일의 output_dir 을 &quot;.&quot; 으로 설정한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_site.yaml 파일은 다음과 같이 될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1724415101290&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: &quot;[id].github.io&quot;
title: &quot;My Blog&quot;
description: |
  My Blog
output_dir: &quot;.&quot;
navbar:
  right:
    - text: &quot;Home&quot;
      href: index.html
    - text: &quot;About&quot;
      href: about.html
output: distill::distill_article
collections:
  posts:
    categories: true
    categories_metadata: true
    authors_metadata: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인의 github 에 [id].github.io 라는 이름의 레포지토리를&amp;nbsp; 만들고 여기에 만든 코드를 push 를 해주면 블로그가 잘 생성된 것을 볼 수 있다. github pages 로 푸시하는 과정은 아래와 같다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1724416011226&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd [home] # 생성한 distill 프로젝트의 홈폴더로 이동한다. 
git init
git add --all
git commit -m 'first commit'
git remote add origin https://github.com/[id]/[id].github.io
git push origin master&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 형태의 기본 Distill 템플릿의 블로그가 생성되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-23 오후 9.12.26.png&quot; data-origin-width=&quot;3730&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcEFt0/btsJdeoffkI/IuvSXIjSgNW3dEiHmrfbK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcEFt0/btsJdeoffkI/IuvSXIjSgNW3dEiHmrfbK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcEFt0/btsJdeoffkI/IuvSXIjSgNW3dEiHmrfbK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcEFt0%2FbtsJdeoffkI%2FIuvSXIjSgNW3dEiHmrfbK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3730&quot; height=&quot;636&quot; data-filename=&quot;스크린샷 2024-08-23 오후 9.12.26.png&quot; data-origin-width=&quot;3730&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* 또는 output_dir 을 docs 로 설정한 후, github pages 설정에서 source 폴더를 docs 폴더로 설정하는 것을 추천한다. &lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-25 오후 5.05.05.png&quot; data-origin-width=&quot;2146&quot; data-origin-height=&quot;1030&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQjyTf/btsJfjVTzPz/k5oNCxqnveqMcaQSlf8ghk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQjyTf/btsJfjVTzPz/k5oNCxqnveqMcaQSlf8ghk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQjyTf/btsJfjVTzPz/k5oNCxqnveqMcaQSlf8ghk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQjyTf%2FbtsJfjVTzPz%2Fk5oNCxqnveqMcaQSlf8ghk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2146&quot; height=&quot;1030&quot; data-filename=&quot;스크린샷 2024-08-25 오후 5.05.05.png&quot; data-origin-width=&quot;2146&quot; data-origin-height=&quot;1030&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;포스트 조금 수정해보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 포스트에 카테고리 붙이기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 블로그에는 오른쪽의 CATEGORIES 메뉴가 없을 것이다. 카테고리를 만드는 방법은 단순히 포스트에 다가 category tag 를 붙이면된다. 카테고리를 직접적으로 지정할 필요없이 포스트에 붙어 있는 태그를 통해 전체 카테고리가 재구성된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1724415651521&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
title: &quot;Welcome to My Blog&quot;
categories:
- Category1
- Category2
- Category3
description: |
  Welcome to our new blog, My Blog. We hope you enjoy 
  reading what we have to say!
author:
  - name: Nora Jones 
    url: https://example.com/norajones
    affiliation: Spacely Sprockets
    affiliation_url: https://example.com/spacelysprokets
date: 08-23-2024
output:
  distill::distill_article:
    self_contained: false
---&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) rmd 를 html 형식으로 변환하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 카테고리를 붙이는 등 rmd 파일을 수정했다면 이를 html 로 변환해줄 필요가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기본적으로 rstudio 에서 build - Install and Restart 를 누르면 빌드된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다만 build 를 통해서는 각 포스트가 html 로 변환되지는 않는다. 포스트를 변환하기 위해서는 각 rmd 파일에 대해 knitr 을 실행해서 rmd 파일을 html 파일로 변환해주면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-23 오후 9.21.15.png&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;988&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5DeRq/btsJfbpIX8q/e3ScBGYGcvqIqfsJ8EOWQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5DeRq/btsJfbpIX8q/e3ScBGYGcvqIqfsJ8EOWQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5DeRq/btsJfbpIX8q/e3ScBGYGcvqIqfsJ8EOWQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5DeRq%2FbtsJfbpIX8q%2Fe3ScBGYGcvqIqfsJ8EOWQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;457&quot; data-filename=&quot;스크린샷 2024-08-23 오후 9.21.15.png&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;988&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knit 를 통해 rmd 파일을 html 파일로 변환함으로써 github pages 를 통해 호스팅할 수 있다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Tools/R</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/625</guid>
      <comments>https://3months.tistory.com/625#entry625comment</comments>
      <pubDate>Fri, 23 Aug 2024 21:28:34 +0900</pubDate>
    </item>
    <item>
      <title>R - ggplot 의 유용한 add-on 패키지 ggrepel</title>
      <link>https://3months.tistory.com/623</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;R&amp;nbsp;-&amp;nbsp;ggplot&amp;nbsp;의&amp;nbsp;유용한&amp;nbsp;add-on&amp;nbsp;패키지&amp;nbsp;ggrepel&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R 에서 ggplot 을 통해 그래프를 그릴 때 불편했던 점이 텍스트 레이블을 차트에 표시하기 어려웠던 점이다. 엑셀로 그래프를 그리는 경우 레이블 옵션을 설정한 뒤 이를 마우스 드래그를 통해서 조정할 수 있으나, ggplot 에서는 hjust, vjust 를 통해 한땀한땀 레이블을 조정하곤했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시나 레이블의 위치를 자동으로 바꿔주는 패키지가 있나 살펴보다가 ggrepel 이라는 패키지가 있다는 것을 알았다. 사용법은 매우 쉽다. 만드려고한 ggplot 에다가 +&amp;nbsp; geom_text_repel() 만 해주면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-22 오후 2.31.06.png&quot; data-origin-width=&quot;2206&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pnZ6I/btsJbHwdbhr/Hma5hnbDSyECaEgcEcwCD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pnZ6I/btsJbHwdbhr/Hma5hnbDSyECaEgcEcwCD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pnZ6I/btsJbHwdbhr/Hma5hnbDSyECaEgcEcwCD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpnZ6I%2FbtsJbHwdbhr%2FHma5hnbDSyECaEgcEcwCD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2206&quot; height=&quot;348&quot; data-filename=&quot;스크린샷 2024-08-22 오후 2.31.06.png&quot; data-origin-width=&quot;2206&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ii9ova8.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zrhez/btsJbse1w1H/epgKCY7iJbQS8NwK7NrLA0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zrhez/btsJbse1w1H/epgKCY7iJbQS8NwK7NrLA0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zrhez/btsJbse1w1H/epgKCY7iJbQS8NwK7NrLA0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/zrhez/btsJbse1w1H/epgKCY7iJbQS8NwK7NrLA0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;225&quot; data-filename=&quot;ii9ova8.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트간의 겹치는 것을 조정해주기도 하지만, line 이나 point 와 같은 그래프의 요소와 텍스트가 겹치는 것을 알아서 조정해주기도 한다. 아래의 line chart 를 보면 point 와 겹치지 않게 text 가 자동으로 위치가 조정된 것을 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;plot_zoom_png (5).png&quot; data-origin-width=&quot;2552&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AgL0d/btsJdp3p6db/tnwKfvPI9w6kfWB3kRffQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AgL0d/btsJdp3p6db/tnwKfvPI9w6kfWB3kRffQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AgL0d/btsJdp3p6db/tnwKfvPI9w6kfWB3kRffQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAgL0d%2FbtsJdp3p6db%2FtnwKfvPI9w6kfWB3kRffQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2552&quot; height=&quot;736&quot; data-filename=&quot;plot_zoom_png (5).png&quot; data-origin-width=&quot;2552&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1724394453920&quot; class=&quot;r&quot; data-ke-language=&quot;r&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ggplot(aes(x=x, y=y, group=g, label=round(t,2)), data=data) +
  geom_line(aes(color=g), size=1) +
  geom_point(aes(color=g)) + 
  theme_bw(base_size=10, base_family = &quot;Kakao Regular&quot;) +
  theme(strip.background =element_rect(fill=&quot;#DEEBFF&quot;),
        strip.text.x = element_text(size = 10),
        legend.position=&quot;right&quot;, 
        plot.title = element_text(hjust = 0.5, size=12), 
        axis.text.x = element_text(size=10),
        legend.text=element_text(size=10),
        legend.title=element_text(size=15),
        panel.grid.minor = element_line(size = 1), 
        panel.grid.major = element_line(size = 1),
        plot.background = element_rect(colour = &quot;#cdcdcd&quot;,size = 0.5)) +
  ggtitle(&quot;Plot Title&quot;) +
  labs(color = 'STRATA') +
  ylab(&quot;Y LAB&quot;) + xlab(&quot;X LAB&quot;)+
  geom_text_repel()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Tools/R</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/623</guid>
      <comments>https://3months.tistory.com/623#entry623comment</comments>
      <pubDate>Thu, 22 Aug 2024 14:32:03 +0900</pubDate>
    </item>
    <item>
      <title>R - code snippet 특정 이름 패턴 파일들을 읽어 합치기</title>
      <link>https://3months.tistory.com/621</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-02 오후 1.43.22.png&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHec0I/btsIUhXWonS/WGEnG4oidDsbzlfNuEg860/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHec0I/btsIUhXWonS/WGEnG4oidDsbzlfNuEg860/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHec0I/btsIUhXWonS/WGEnG4oidDsbzlfNuEg860/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHec0I%2FbtsIUhXWonS%2FWGEnG4oidDsbzlfNuEg860%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;172&quot; data-filename=&quot;스크린샷 2024-08-02 오후 1.43.22.png&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;quantile_output 이라는 폴더 아래에 6월 1일~6월 30일 사이의 30개의 csv 파일이 존재한다. 각 파일들은 같은 형식이며, 각기 다른 날짜에 집계됐다는 점만 다르다. 이 경우에 1) Sys.glob 을 이용해 해당 파일들의 경로를 벡터 형태로 만든 후에 2) lapply 를 통해 각 파일들을 읽어 list of dataframes 를 만들고, 3) 마지막으로 bind_rows 를 통해 dataframe 으로 만들어주면 된다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722573786156&quot; class=&quot;r&quot; data-ke-language=&quot;r&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;result_files &amp;lt;- Sys.glob(&quot;quantile_output/quantile_2024-06*&quot;)
result_list &amp;lt;- lapply(result_files, function(x) read_csv(x))
merged &amp;lt;- dplyr::bind_rows(result_list)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Tools/R</category>
      <author>Deepplay</author>
      <guid isPermaLink="true">https://3months.tistory.com/621</guid>
      <comments>https://3months.tistory.com/621#entry621comment</comments>
      <pubDate>Fri, 2 Aug 2024 13:48:24 +0900</pubDate>
    </item>
  </channel>
</rss>