# ChatCompletion

## OpenAI: ChatCompletion

```python
#%pip install python-dotenv
```

```python
import os
from dotenv import load_dotenv  

!echo "OPENAI_API_KEY=<YOUR API KEY>" >> .env
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
```

### 1. Client 생성

* `client` 는 OpenAI 모듈로 생성된 인스턴스 입니다.

\[주의] 아래의 코드에서 오류가 난다면 API 키의 오류일 가능성이 높습니다.

```python
#%pip install openai==1.12.0
```

```python
import openai
openai.__version__
```

```
'1.12.0'
```

```python
from openai import OpenAI

client = OpenAI(api_key=api_key)
client
```

```
<openai.OpenAI at 0x7f5797d12f10>
```

### 2. ChatCompletions

* API Reference: https://platform.openai.com/docs/api-reference/chat

**주요 파라미터**

* `messages`: 지금까지의 대화를 구성하는 메시지 목록입니다.
* `frequency_penalty`: -2.0에서 2.0 사이의 숫자. 양수 값은 지금까지 텍스트에 나타난 기존 빈도에 따라 새로운 토큰에 불이익을 주어 모델이 같은 줄을 그대로 반복할 가능성을 낮춥니다.
* `max_tokens`: 생성할 수 있는 최대 토큰 수입니다. 입력 토큰과 생성된 토큰의 총 길이는 모델의 컨텍스트 길이에 의해 제한됩니다.
* `n`: 각 입력 메시지에 대해 생성할 선택지(choices) 수입니다. \[주의] 모든 선택 항목에서 생성된 토큰 수에 따라 요금이 부과된다는 점에 유의하세요. 비용을 최소화하려면 n을 1로 유지하세요.
* `presence_penalty`: -2.0에서 2.0 사이의 숫자. 값이 양수이면 지금까지 텍스트에 등장한 토큰에 따라 새로운 토큰에 불이익을 주므로 모델이 새로운 주제에 대해 이야기할 가능성이 높아집니다.
* `response_format`: 모델이 출력해야 하는 형식을 지정하는 객체입니다. `gpt-4-turbo-preview` 및 `gpt-3.5-turbo` 과 호환됩니다. `{ "type": "json_object" }` 로 설정하면 JSON 모드가 활성화되어 모델이 생성하는 메시지가 유효한 JSON임을 보장합니다.
* `seed`: 이 기능을 지정하면 시스템이 결정론적으로 샘플링하여 동일한 시드와 매개변수를 사용한 반복 요청이 동일한 결과를 반환하도록 최선을 다할 것입니다. 결정론은 보장되지 않으며, `system_fingerprint` 응답 매개변수를 참조하여 백엔드의 변경 사항을 모니터링해야 합니다.
* `temperature`: 0에서 2 사이에서 사용할 샘플링 온도입니다. 0.8과 같이 값이 높으면 출력이 더 무작위적이고, 0.2와 같이 값이 낮으면 더 집중적이고 결정론적인 출력이 됩니다. 일반적으로 이 값이나 `top_p` 중 하나만 변경하는 것이 좋지만 둘 다 변경하지는 않는 것이 좋습니다.
* `top_p`: `temperature` 를 이용한 샘플링의 대안으로, 핵 샘플링이라고 하며, 모델이 `top_p` 확률을 가진 토큰의 결과를 고려하는 방식입니다. 따라서 0.1은 상위 10% 확률을 구성하는 토큰만 고려한다는 의미입니다. 일반적으로 이 값이나 `temperature` 중 하나를 변경하는 것이 좋지만 둘 다 변경하는 것은 권장하지 않습니다.

```python
completion = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
        {
            "role": "system",
            "content": "당신은 파이썬 프로그래머입니다.",
        },
        {
            "role": "user",
            "content": "피보나치 수열을 생성하는 파이썬 프로그램을 작성해주세요.",
        },
    ],
)
```

```python
print(completion.choices[0].message.content)
```

````
피보나치 수열은 이전 두 수를 더한 값을 다음 항으로 하는 수열입니다. 이를 파이썬 프로그램으로 나타내면 다음과 같이 작성할 수 있습니다.

```python
def fibonacci(n):
    fibonacci_seq = [0, 1]
    for i in range(2, n):
        next_num = fibonacci_seq[i-1] + fibonacci_seq[i-2]
        fibonacci_seq.append(next_num)
    return fibonacci_seq

n = int(input("피보나치 수열을 몇 개 생성하시겠습니까? "))
fibonacci_sequence = fibonacci(n)
print(fibonacci_sequence)
```

이 프로그램을 실행하면 사용자로부터 피보나치 수열을 생성할 개수를 입력받고, 해당 개수만큼의 피보나치 수열을 생성하여 출력합니다.
````

### 2. 스트리밍(Streaming)

* 스트리밍은 실시간으로 데이터를 전송하고 수신하는 프로세스로 동적으로 생성
* `create()` 함수내에 stream=True 옵션을 지정
* 토큰 단위로 실시간 출력을 위해서는 `completion` 을 순회하면서, `choices.delta.content` 를 출력

```python
from openai import OpenAI

client = OpenAI()

completion = client.chat.completions.create(
    model="gpt-4-turbo-preview",
    messages=[
        {
            "role": "system",
            "content": "당신은 파이썬 프로그래머입니다.",
        },
        {
            "role": "user",
            "content": "피보나치 수열을 생성하는 파이썬 프로그램을 작성해주세요.",
        },
    ],
    stream=True,  # 스트림 모드 활성화
)

final_answer = []

# 스트림 모드에서는 completion.choices 를 반복문으로 순회
for chunk in completion:
    # chunk 를 저장
    chunk_content = chunk.choices[0].delta.content
    # chunk 가 문자열이면 final_answer 에 추가
    if isinstance(chunk_content, str):
        final_answer.append(chunk_content)
        # 토큰 단위로 실시간 답변 출력
        print(chunk_content, end="")
```

````
물론입니다. 피보나치 수열을 생성하는 파이썬 프로그램의 예시를 아래에 제공드립니다. 이 코드는 재귀 함수와 반복문을 사용한 두 가지 방법으로 피보나치 수열을 생성합니다.

### 1. 재귀 함수를 사용한 방법:

재귀 함수를 사용하는 방법은 간단하지만 큰 수에 대해서는 비효율적일 수 있습니다. 이는 재귀 호출에 따른 상당한 메모리 사용과 중복 계산 때문입니다.

```python
def fibonacci_recursive(n):
    if n <= 1:
        return n
    else:
        return(fibonacci_recursive(n-1) + fibonacci_recursive(n-2))

# 예시로 첫 10개의 피보나치 수열을 출력해봅시다.
n_terms = 10

if n_terms <= 0:
    print("정수를 입력해주세요.")
else:
    print("피보나치 수열:")
    for i in range(n_terms):
        print(fibonacci_recursive(i))
```

### 2. 반복문을 사용한 방법:

반복문을 사용하는 방법은 더 효율적입니다. 이는 이전 두 수만 저장하고 반복적으로 계산함으로써 필요한 모든 피보나치 수를 빠르게 생성합니다.

```python
def fibonacci_iterative(n):
    a, b = 0, 1
    for i in range(n):
        a, b = b, a + b
    return a

# 예시로 첫 10개의 피보나치 수열을 출력해봅시다.
n_terms = 10

print("피보나치 수열:")
for i in range(n_terms):
    print(fibonacci_iterative(i))
```

두 번째 방법은 실제로 큰 수를 처리할 때 훨씬 더 효과적입니다.
````

```python
# 전체 답변인 final_answer 를 문자열로 변환하여 출력
final_answer = "".join(final_answer)

print(final_answer)
```

````
물론입니다. 피보나치 수열을 생성하는 파이썬 프로그램의 예시를 아래에 제공드립니다. 이 코드는 재귀 함수와 반복문을 사용한 두 가지 방법으로 피보나치 수열을 생성합니다.

### 1. 재귀 함수를 사용한 방법:

재귀 함수를 사용하는 방법은 간단하지만 큰 수에 대해서는 비효율적일 수 있습니다. 이는 재귀 호출에 따른 상당한 메모리 사용과 중복 계산 때문입니다.

```python
def fibonacci_recursive(n):
    if n <= 1:
        return n
    else:
        return(fibonacci_recursive(n-1) + fibonacci_recursive(n-2))

# 예시로 첫 10개의 피보나치 수열을 출력해봅시다.
n_terms = 10

if n_terms <= 0:
    print("정수를 입력해주세요.")
else:
    print("피보나치 수열:")
    for i in range(n_terms):
        print(fibonacci_recursive(i))
```

### 2. 반복문을 사용한 방법:

반복문을 사용하는 방법은 더 효율적입니다. 이는 이전 두 수만 저장하고 반복적으로 계산함으로써 필요한 모든 피보나치 수를 빠르게 생성합니다.

```python
def fibonacci_iterative(n):
    a, b = 0, 1
    for i in range(n):
        a, b = b, a + b
    return a

# 예시로 첫 10개의 피보나치 수열을 출력해봅시다.
n_terms = 10

print("피보나치 수열:")
for i in range(n_terms):
    print(fibonacci_iterative(i))
```

두 번째 방법은 실제로 큰 수를 처리할 때 훨씬 더 효과적입니다.
````

### 3. 연쇄적인 대화

`client.chat.completions.create()` 함수는 자체적으로 대화를 저장하는 기능이 없습니다.

따라서, 이전 문맥(context)을 기억하면서 대화를 이어나가기 위해서는(일반적인 ChatBot을 생각하시면 됩니다) 다음과 같이 `messages` 옵션에 메시지를 추가해야 합니다.

messages는 대화의 각 부분을 구성하는 데 사용되며, 각 메시지는 "role"과 "content"라는 두 가지 주요 요소를 포함하는 딕셔너리 형태로 되어 있습니다. 이 구조를 사용하면 복잡한 대화 흐름을 더 명확하게 관리하고 조작할 수 있습니다.

각 "role"의 의미는 다음과 같습니다:

* "system": 시스템 전역 설정에 대한 지시문을 포함합니다. 예를 들어, 모델에 특정한 페르소나(persona)를 부여하거나, 대화의 맥락을 설정하는 데 사용됩니다.
* "user": 사용자의 입력을 나타냅니다. 이는 대화에서 사용자가 질문하거나 요청한 내용을 담고 있습니다.
* "assistant": AI 모델(예: ChatGPT)의 응답을 나타냅니다. 이는 모델이 생성한 답변이나 정보를 포함합니다.

```python
completion = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
        {
            "role": "system",
            "content": "You are a helpful assistant. You must answer in Korean.",
        },
        {
            "role": "user",
            "content": "대한민국의 수도는 어디인가요?",
        },
    ],
)

print(completion.choices[0].message.content)
```

```
대한민국의 수도는 서울입니다.
```

```python
def ask(question):
    completion = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {
                "role": "system",
                "content": "You are a helpful assistant. You must answer in Korean.",
            },
            {
                "role": "user",
                "content": question,  # 사용자의 질문을 입력
            },
        ],
    )
    # 답변을 반환
    return completion.choices[0].message.content
```

```python
# 첫 번째 질문
ask("대한민국의 수도는 어디인가요?")
```

```
'대한민국의 수도는 서울 입니다.'
```

```python
# 두 번째 질문
ask("영어로 답변해 주세요")
```

```
'저는 한국어로만 응답할 수 있습니다. 궁금한 것이 있으시면 언제든지 물어보세요!'
```

위의 답변에서 보듯이, GPT가 **다음 질문에 대한 답변을 잘못했습니다.** 이는 이전 대화내용에 대한 **저장을 하지 않았기 때문** 입니다.

이를 해결하기 위해, 대화의 연속성을 유지하는 로직을 추가해야 합니다. 대화의 각 부분(사용자의 질문과 AI의 응답)을 messages 리스트에 순차적으로 추가함으로써, 챗봇은 이전의 대화 내용을 참조하여 적절한 답변을 생성할 수 있습니다.

```python
completion = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
        {
            "role": "system",
            "content": "You are a helpful assistant. You must answer in Korean.",
        },
        {
            "role": "user",
            "content": "대한민국의 수도는 어디인가요?",  # 첫 번째 질문
        },
        {
            "role": "assistant",
            "content": "대한민국의 수도는 서울입니다.",  # 첫 번째 답변
        },
        {
            "role": "user",
            "content": "이전의 답변을 영어로 번역해 주세요.",  # 두 번째 질문
        },
    ],
)

# 두 번째 답변을 출력
print(completion.choices[0].message.content)
```

```
The capital of South Korea is Seoul.
```

#### 사용자 정의 함수화

```python
def ask(question, message_history=[], model="gpt-3.5-turbo"):
    if len(message_history) == 0:
        # 최초 질문
        message_history.append(
            {
                "role": "system",
                "content": "You are a helpful assistant. You must answer in Korean.",
            }
        )

    # 사용자 질문 추가
    message_history.append(
        {
            "role": "user",
            "content": question,
        },
    )

    # GPT에 질문을 전달하여 답변을 생성
    completion = client.chat.completions.create(
        model=model,
        messages=message_history,
    )

    # 사용자 질문에 대한 답변을 추가
    message_history.append(
        {"role": "assistant", "content": completion.choices[0].message.content}
    )

    return message_history
```

```python
# 최초 질문
message_history = ask("양자역학에 대해서 쉽게 설명해 주세요", message_history=[])

# 최초 답변
print(message_history[-1])
```

```
{'role': 'assistant', 'content': '양자역학은 아주 작은 입자들의 행동을 연구하는 물리학의 한 분야입니다. 이론은 입자가 파동이자 입자이며, 위치나 운동량을 정확하게 동시에 알 수 없다는 불확정성 원리에 근거합니다. 또한 입자들의 상태를 표현하는 파동함수를 사용하여 확률적으로 입자의 위치나 속도를 예측합니다.양자역학은 매우 현실적이고 정확한 설명을 제공하지만, 우리가 일상에서 경험하는 대부분의 상황에는 적용하기 어렵습니다.'}
```

```python
message_history
```

```
[{'role': 'system',
  'content': 'You are a helpful assistant. You must answer in Korean.'},
 {'role': 'user', 'content': '양자역학에 대해서 쉽게 설명해 주세요'},
 {'role': 'assistant',
  'content': '양자역학은 아주 작은 입자들의 행동을 연구하는 물리학의 한 분야입니다. 이론은 입자가 파동이자 입자이며, 위치나 운동량을 정확하게 동시에 알 수 없다는 불확정성 원리에 근거합니다. 또한 입자들의 상태를 표현하는 파동함수를 사용하여 확률적으로 입자의 위치나 속도를 예측합니다.양자역학은 매우 현실적이고 정확한 설명을 제공하지만, 우리가 일상에서 경험하는 대부분의 상황에는 적용하기 어렵습니다.'}]
```

```python
# 두 번째 질문
message_history = ask(
    "이전의 내용을 영어로 답변해 주세요", message_history=message_history
)
# 두 번째 답변
print(message_history[-1])
```

```
{'role': 'assistant', 'content': 'Quantum mechanics is a field of physics that studies the behavior of very small particles. The theory states that particles are both waves and particles, and that it is impossible to know the exact position and momentum of a particle simultaneously due to the uncertainty principle. Using wave functions to describe the states of particles, quantum mechanics predicts the position and velocity of particles probabilistically. Quantum mechanics provides a very realistic and accurate description, but it is difficult to apply to most situations we experience in everyday life.'}
```

```python
message_history
```

```
[{'role': 'system',
  'content': 'You are a helpful assistant. You must answer in Korean.'},
 {'role': 'user', 'content': '양자역학에 대해서 쉽게 설명해 주세요'},
 {'role': 'assistant',
  'content': '양자역학은 아주 작은 입자들의 행동을 연구하는 물리학의 한 분야입니다. 이론은 입자가 파동이자 입자이며, 위치나 운동량을 정확하게 동시에 알 수 없다는 불확정성 원리에 근거합니다. 또한 입자들의 상태를 표현하는 파동함수를 사용하여 확률적으로 입자의 위치나 속도를 예측합니다.양자역학은 매우 현실적이고 정확한 설명을 제공하지만, 우리가 일상에서 경험하는 대부분의 상황에는 적용하기 어렵습니다.'},
 {'role': 'user', 'content': '이전의 내용을 영어로 답변해 주세요'},
 {'role': 'assistant',
  'content': 'Quantum mechanics is a field of physics that studies the behavior of very small particles. The theory states that particles are both waves and particles, and that it is impossible to know the exact position and momentum of a particle simultaneously due to the uncertainty principle. Using wave functions to describe the states of particles, quantum mechanics predicts the position and velocity of particles probabilistically. Quantum mechanics provides a very realistic and accurate description, but it is difficult to apply to most situations we experience in everyday life.'}]
```

이번에는 이전 대화내용을 `message_history` 에 저장하여 전달하였습니다. 이전 대화내용을 저장하면서 `message_history` 를 통해 대화를 이어나갈 수 있습니다.

### 4. json\_object 답변형식

이번에는 GPT 의 출력 형식을 지정하는 방법을 살펴보겠습니다. 다음의 예시는 프롬프트를 활용하여 답변을 JSON 형식으로 받는 예제 입니다.

```python
completion = client.chat.completions.create(
    model="gpt-3.5-turbo-1106",
    messages=[
        {
            "role": "system",
            # 답변 형식을 JSON 으로 받기 위해 프롬프트에 JSON 형식을 지정
            "content": "You are a helpful assistant designed to output JSON. You must answer in Korean.",
        },
        {
            "role": "user",
            "content": "대한민국의 수도는 어디인가요?",
        },
    ],
    response_format={"type": "json_object"},  # 답변 형식을 JSON 으로 지정
)

print(completion.choices[0].message.content)
```

```
{"answer": "서울"}
```

```python
response = client.chat.completions.create(
    model="gpt-3.5-turbo-1106",
    response_format={"type": "json_object"},
    messages=[
        {
            "role": "system",
            "content": "You are a helpful assistant designed to output JSON.",
        },
        {
            "role": "user",
            "content": "통계를 주제로 4지선다형 객관식 문제를 만들어주세요.",
        },
    ],
    temperature=0.5,
    max_tokens=300,
)
print(response.choices[0].message.content)
```

```
{
  "question": "다음 중 가설검정에서 기각역이란 무엇인가요?",
  "options": [
    "귀무가설을 기각하는 영역",
    "귀무가설을 채택하는 영역",
    "표본평균이 속하는 영역",
    "가설검정을 수행하지 않는 영역"
  ]
}
```

`response_format={"type": "json_object"}` 으로 출력시 json 형태로 출력되는 것을 확인할 수 있습니다.

`json` 형식으로 출력 값을 받으면 데이터베이스에 저장하거나 파일형태로 저장하는데 용이합니다.

```python
response = client.chat.completions.create(
    model="gpt-3.5-turbo-1106",
    response_format={"type": "json_object"},
    messages=[
        {
            "role": "system",
            "content": "You are a helpful assistant designed to output JSON.",
        },
        {
            "role": "user",
            "content": "통계를 주제로 4지선다형 객관식 문제를 만들어주세요. 정답은 index 번호로 알려주세요. "
            "난이도는 [상, 중, 하] 중 하나로 표기해 주세요.",
        },
    ],
    temperature=0.5,
    max_tokens=300,
    n=5,
)
```

```python
for res in response.choices:
    print(res.message.content)
```

```
{
  "question": "다음 중 통계학의 개념에 대한 설명으로 옳지 않은 것은?",
  "options": [
    "1. 통계학은 데이터를 수집, 분석, 해석하는 학문이다.",
    "2. 통계학은 불확실성을 다루는 학문이다.",
    "3. 통계학은 오직 정확한 결과만을 제시한다.",
    "4. 통계학은 의사결정에 도움을 주는 학문이다."
  ],
  "answer": 2,
  "difficulty": "하"
}
{
  "question": "다음 중 통계학에서 사용되는 가설검정 방법이 아닌 것은?",
  "options": ["t-검정", "ANOVA", "카이제곱 검정", "상관분석"],
  "answer_index": 3,
  "difficulty": "중"
}
{
  "question": "다음 중 통계학의 기초 개념으로 옳지 않은 것은 무엇인가요?",
  "options": [
    "평균(mean)",
    "중앙값(median)",
    "표준편차(standard deviation)",
    "확률(probability)"
  ],
  "answer_index": 3,
  "difficulty": "중"
}
{
  "question": "다음 중 통계 분석 방법이 아닌 것은?",
  "options": ["t-검정", "회귀분석", "클러스터 분석", "상관분석"],
  "answer_index": 2,
  "difficulty": "중"
}
{
  "question": "다음 중 통계학에서 사용되는 중심 경향성을 나타내는 척도는 무엇인가요?",
  "options": ["표준편차", "평균", "분산", "최빈값"],
  "answer_index": 1,
  "difficulty": "중"
}
```

#### JSON 라이브러리 사용

```python
import json

# JSON 형식의 답변을 파이썬 객체로 변환
json_obj = json.loads(res.message.content)
json_obj
```

```
{'question': '다음 중 통계학에서 사용되는 중심 경향성을 나타내는 척도는 무엇인가요?',
 'options': ['표준편차', '평균', '분산', '최빈값'],
 'answer_index': 1,
 'difficulty': '중'}
```

```python
# type 함수로 json_obj 의 타입을 확인: dict 타입
type(json_obj)
```

```
dict
```

```python
# 모든 json 형식의 답변을 파이썬 객체로 변환
json_result = [json.loads(res.message.content) for res in response.choices]
json_result
```

```
[{'question': '다음 중 통계학의 개념에 대한 설명으로 옳지 않은 것은?',
  'options': ['1. 통계학은 데이터를 수집, 분석, 해석하는 학문이다.',
   '2. 통계학은 불확실성을 다루는 학문이다.',
   '3. 통계학은 오직 정확한 결과만을 제시한다.',
   '4. 통계학은 의사결정에 도움을 주는 학문이다.'],
  'answer': 2,
  'difficulty': '하'},
 {'question': '다음 중 통계학에서 사용되는 가설검정 방법이 아닌 것은?',
  'options': ['t-검정', 'ANOVA', '카이제곱 검정', '상관분석'],
  'answer_index': 3,
  'difficulty': '중'},
 {'question': '다음 중 통계학의 기초 개념으로 옳지 않은 것은 무엇인가요?',
  'options': ['평균(mean)',
   '중앙값(median)',
   '표준편차(standard deviation)',
   '확률(probability)'],
  'answer_index': 3,
  'difficulty': '중'},
 {'question': '다음 중 통계 분석 방법이 아닌 것은?',
  'options': ['t-검정', '회귀분석', '클러스터 분석', '상관분석'],
  'answer_index': 2,
  'difficulty': '중'},
 {'question': '다음 중 통계학에서 사용되는 중심 경향성을 나타내는 척도는 무엇인가요?',
  'options': ['표준편차', '평균', '분산', '최빈값'],
  'answer_index': 1,
  'difficulty': '중'}]
```

아래는 Pandas 라이브러리를 사용하여 Python dictionary를 데이터프레임으로 변환하는 코드입니다.

```python
import pandas as pd

# 데이터프레임으로 변환
df = pd.DataFrame(json_result)
df.head()
```

|   | question                                | options                                            | answer | difficulty | answer\_index |
| - | --------------------------------------- | -------------------------------------------------- | ------ | ---------- | ------------- |
| 0 | 다음 중 통계학의 개념에 대한 설명으로 옳지 않은 것은?         | \[1. 통계학은 데이터를 수집, 분석, 해석하는 학문이다., 2. 통계학은 불확실성... | 2.0    | 하          | NaN           |
| 1 | 다음 중 통계학에서 사용되는 가설검정 방법이 아닌 것은?         | \[t-검정, ANOVA, 카이제곱 검정, 상관분석]                      | NaN    | 중          | 3.0           |
| 2 | 다음 중 통계학의 기초 개념으로 옳지 않은 것은 무엇인가요?       | \[평균(mean), 중앙값(median), 표준편차(standard deviatio... | NaN    | 중          | 3.0           |
| 3 | 다음 중 통계 분석 방법이 아닌 것은?                   | \[t-검정, 회귀분석, 클러스터 분석, 상관분석]                       | NaN    | 중          | 2.0           |
| 4 | 다음 중 통계학에서 사용되는 중심 경향성을 나타내는 척도는 무엇인가요? | \[표준편차, 평균, 분산, 최빈값]                               | NaN    | 중          | 1.0           |

```python
# 데이터프레임을 csv 파일로 저장
df.to_csv("stats_quiz.csv", index=False)
```

```python
# 데이터프레임을 엑셀 파일로 저장
df.to_excel("stats_quiz.xlsx", index=False)
```
