由 AI 驱动
  • 主页
  • 手册
    • SQL 手册
    • R 手册
    • Python 手册
    • 机器学习手册
    • TensorFlow 手册
    • AI 手册
  • 博客
  • 简历
  • 中文/EN
    • 中文
    • English

On this page

  • 安装包 (install package)
  • 加载包 (load package)
  • 下载数据 (download data)
  • 设置模型 (set up model)
  • 情感分析 (Sentiment)
  • 总结 (Summarize)
  • 分类 (Classify)
  • 提取 (Extract)
  • 验证 (Verify)
  • 翻译 (Translate)
  • 自定义提示词 (Custom prompt)
  • 调用 Vector
  • 参考资料 (Reference)

mall包- AI处理数据表

AI
Author

Tony D

Published

March 24, 2026

使用大型语言模型 (LLM) 对您的数据运行自然语言处理 (NLP) 操作。它利用 LLM 的通用语言训练来获取预测,从而消除了训练新 NLP 模型的需要。mall 可用于 R 和 Python。

安装包 (install package)

  • Python
  • R
pip install mlverse-mall
pip install chatlas
install.packages("mall")
install.packages("ellmer")

加载包 (load package)

保存 Openrouter API OPENROUTER_API_KEY 到 .env 文件

  • Python
  • R
import os
import mall
from chatlas import ChatOpenRouter
from dotenv import load_dotenv
import polars as pl
load_dotenv()
True
library(mall)
library(ellmer)
library(dotenv)
library(tidyverse)
# This looks for a .env file in the current working directory
load_dot_env()

下载数据 (download data)

数据来源: 豆瓣电影 - 飞驰人生3

  • Python
  • R
import requests
from bs4 import BeautifulSoup
import time
import random

file_path = "douban_comments.csv"

if not os.path.exists(file_path):
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
        "Referer": "https://movie.douban.com/subject/37311135/",
    }
    
    all_comments = []
    # 抓取前3页,共60条评论
    for page in range(3):
        url = f"https://movie.douban.com/subject/37311135/comments?start={page * 20}&limit=20&status=P&sort=new_score"
        resp = requests.get(url, headers=headers, timeout=10)
        soup = BeautifulSoup(resp.text, "html.parser")
        
        for item in soup.select("div.comment-item"):
            comment_text = item.select_one("span.short")
            rating_span = item.select_one("span.comment-info span.rating")
            user_span = item.select_one("span.comment-info a")
            
            if comment_text:
                # 从 class 如 'allstar50' 提取评分
                rating = ""
                if rating_span:
                    cls = [c for c in rating_span.get("class", []) if c.startswith("allstar")]
                    if cls:
                        rating = str(int(cls[0].replace("allstar", "")) // 10)
                
                all_comments.append({
                    "user": user_span.text.strip() if user_span else "",
                    "rating": rating,
                    "comment": comment_text.text.strip()
                })
        
        time.sleep(random.uniform(2, 4))  # 礼貌延迟
    
    pl.DataFrame(all_comments).write_csv(file_path)
    print(f"已下载 {len(all_comments)} 条评论")
else:
    print(f"文件已存在: {file_path}")
文件已存在: douban_comments.csv
reviews = pl.read_csv(file_path).head(3)
reviews
shape: (3, 3)
user rating comment
str i64 str
"Corleone" 4 "三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。"
"壹安²" 4 "韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。"
"阿暖" 3 "韩寒你也看过F1"
library(rvest)

file_path <- "douban_comments.csv"

if (!file.exists(file_path)) {
  all_comments <- data.frame(user = character(), rating = character(), comment = character())
  
  for (page in 0:2) {
    url <- paste0("https://movie.douban.com/subject/37311135/comments?start=", page * 20, "&limit=20&status=P&sort=new_score")
    
    page_html <- read_html(url,
      user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
    )
    
    items <- page_html %>% html_elements("div.comment-item")
    
    for (item in items) {
      comment_text <- item %>% html_element("span.short") %>% html_text2()
      user_name <- item %>% html_element("span.comment-info a") %>% html_text2()
      rating_el <- item %>% html_element("span.comment-info span.rating")
      
      rating <- ""
      if (!is.na(rating_el)) {
        cls <- rating_el %>% html_attr("class")
        rating_num <- gsub(".*allstar(\\d+).*", "\\1", cls)
        rating <- as.character(as.integer(rating_num) %/% 10)
      }
      
      if (!is.na(comment_text)) {
        all_comments <- rbind(all_comments, data.frame(
          user = ifelse(is.na(user_name), "", user_name),
          rating = rating,
          comment = comment_text
        ))
      }
    }
    
    Sys.sleep(runif(1, 2, 4))
  }
  
  write.csv(all_comments, file_path, row.names = FALSE)
  cat("已下载", nrow(all_comments), "条评论\n")
} else {
  cat("文件已存在:", file_path, "\n")
}
文件已存在: douban_comments.csv 
reviews <- head(read.csv(file_path), 3)
reviews
      user rating                                                comment
1 Corleone      4       三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。
2    壹安²      4 韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。
3     阿暖      3                                         韩寒你也看过F1

设置模型 (set up model)

  • Python
  • R
chat = ChatOpenRouter(base_url='https://openrouter.ai/api/v1',
                      api_key=os.getenv("OPENROUTER_API_KEY"),
                      model="minimax/minimax-m2.7"
                      )
reviews.llm.use(chat)
{'backend': 'chatlas', 'chat': <Chat OpenRouter/minimax/minimax-m2.7 turns=0 tokens=0/0>
, '_cache': '_mall_cache'}
chat = chat_openrouter(api_key=Sys.getenv("OPENROUTER_API_KEY"),
                      model="minimax/minimax-m2.7"
                      )
llm_use(chat)

情感分析 (Sentiment)

根据文本自动返回“正面”(positive)、“负面”(negative) 或 “中性”(neutral)。

  • Python
  • R
reviews.llm.sentiment("comment")
shape: (3, 4)
user rating comment sentiment
str i64 str str
"Corleone" 4 "三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。" "positive"
"壹安²" 4 "韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。" "positive"
"阿暖" 3 "韩寒你也看过F1" "neutral"
reviews |>
  llm_sentiment(comment)
      user rating                                                comment
1 Corleone      4       三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。
2    壹安²      4 韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。
3     阿暖      3                                         韩寒你也看过F1
  .sentiment
1   positive
2   positive
3    neutral

总结 (Summarize)

可能需要减少给定文本中的字数。通常是为了更容易理解其意图。该函数有一个参数来控制输出的最大字数 (max_words):

  • Python
  • R
reviews.llm.summarize("comment", 10)
shape: (3, 4)
user rating comment summary
str i64 str str
"Corleone" 4 "三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。" "three parts follow low point o…
"壹安²" 4 "韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。" "han han true chinese hero stor…
"阿暖" 3 "韩寒你也看过F1" "yes han han is a professional …
reviews |>
  llm_summarize(comment, max_words = 10)
      user rating                                                comment
1 Corleone      4       三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。
2    壹安²      4 韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。
3     阿暖      3                                         韩寒你也看过F1
                                                         .summary
1 lows opportunities scapegoating growth champion pattern repeats
2                  韩寒 写 真正 的 中国 侠客 故事,结局 干净 利落
3                                                是的,我也看过f1

分类 (Classify)

使用 LLM 将文本归类到您提供的选项之一:

  • Python
  • R
reviews.llm.classify("comment", ["推荐", "不推荐"])
shape: (3, 4)
user rating comment classify
str i64 str str
"Corleone" 4 "三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。" "不推荐"
"壹安²" 4 "韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。" "推荐"
"阿暖" 3 "韩寒你也看过F1" "不推荐"
reviews |>
  llm_classify(comment, c("推荐", "不推荐"))
      user rating                                                comment
1 Corleone      4       三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。
2    壹安²      4 韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。
3     阿暖      3                                         韩寒你也看过F1
  .classify
1      推荐
2      推荐
3    不推荐

提取 (Extract)

最有趣的用例之一,使用自然语言,我们可以告诉 LLM 返回文本的特定部分。在以下示例中,我们请求 LLM 从影评中提取”观影感受”。LLM 会理解该词的含义,并在文本中寻找与之相关的内容。

  • Python
  • R
reviews.llm.extract("comment", "观影感受")
shape: (3, 4)
user rating comment extract
str i64 str str
"Corleone" 4 "三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。" "惊叹"
"壹安²" 4 "韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。" "赞叹"
"阿暖" 3 "韩寒你也看过F1" "好奇"
reviews |>
  llm_extract(comment, "观影感受")
      user rating                                                comment
1 Corleone      4       三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。
2    壹安²      4 韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。
3     阿暖      3                                         韩寒你也看过F1
                          .extract
1                             绝了
2     真正的中国侠客故事,干净利落
3 文本中未包含任何观影感受的表述。

验证 (Verify)

该函数允许您根据提供的文本检查和确认陈述是否为真。默认情况下,它将返回 1 表示“是”,0 表示“否”。这可以被自定义。

  • Python
  • R
reviews.llm.verify("comment", "是否影评?")
shape: (3, 4)
user rating comment verify
str i64 str i8
"Corleone" 4 "三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。" 1
"壹安²" 4 "韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。" 1
"阿暖" 3 "韩寒你也看过F1" 0
reviews |>
  llm_verify(comment, "是否影评?")
      user rating                                                comment
1 Corleone      4       三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。
2    壹安²      4 韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。
3     阿暖      3                                         韩寒你也看过F1
  .verify
1       1
2       1
3       0

翻译 (Translate)

顾名思义,此函数会将文本翻译成指定的语言。非常棒的一点是,您无需指定源文本的语言。只需定义目标语言即可。翻译的准确性将取决于 LLM。

  • Python
  • R
reviews.llm.translate("comment", "english")
shape: (3, 4)
user rating comment translation
str i64 str str
"Corleone" 4 "三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。" "All three parts follow the sam…
"壹安²" 4 "韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。" "Han Han wrote a true Chinese x…
"阿暖" 3 "韩寒你也看过F1" "Han Han, you've watched F1 too"
reviews |>
  llm_translate(comment, "English")
      user rating                                                comment
1 Corleone      4       三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。
2    壹安²      4 韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。
3     阿暖      3                                         韩寒你也看过F1
                                                                                                                                                  .translation
1 It's incredible how all three parts follow the exact same storyline arc: low point – opportunity – being scapegoated – self-improvement – becoming champion.
2                            Han Han wrote a genuine Chinese chivalric tale, and once the deed was done, he brushed off his robe and left, cleanly victorious.
3                                                                                                                             Han Han, you have watched F1 too

自定义提示词 (Custom prompt)

可以向 LLM 传递您自己的提示词,并让 mall 针对每个文本条目运行它:

  • Python
  • R
my_prompt = (
    "回答一个问题。"
    "只返回答案,不需要解释"
    "可接受的答案是 '值得看', '不值得看'"
    "根据以下影评内容判断,这部电影值不值得看:"
)

reviews.llm.custom("comment", prompt = my_prompt)
shape: (3, 4)
user rating comment custom
str i64 str str
"Corleone" 4 "三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。" "值得看"
"壹安²" 4 "韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。" "值得看"
"阿暖" 3 "韩寒你也看过F1" "值得看"
my_prompt <- paste(
  "回答一个问题。",
  "只返回答案,不需要解释",
  "可接受的答案是 '值得看', '不值得看'",
  "根据以下影评内容判断,这部电影值不值得看:"
)

reviews |>
  llm_custom(comment, my_prompt)
      user rating                                                comment
1 Corleone      4       三部都是低谷–机会–背锅–自强–冠军的剧情也是绝了。
2    壹安²      4 韩寒写了部真正的中国侠客故事,事了拂衣去,干干净净赢。
3     阿暖      3                                         韩寒你也看过F1
     .pred
1   值得看
2   值得看
3 无法判断

调用 Vector

它也可以接收 Vector 作为输入。

  • Python
  • R
# Pass it to a new LLMVec
from mall import LLMVec
llm = LLMVec(chat)    
llm.sentiment(["我很高兴","我不高兴"])
['positive', 'negative']
llm_vec_sentiment(c("我很高兴","我不高兴"))
[1] "positive" "negative"

参考资料 (Reference)

https://mlverse.github.io/mall/

https://posit-dev.github.io/chatlas/

Source Code
---
title: "mall包- AI处理数据表"
author: "Tony D"
date: "2026-03-24"
categories: [AI]
image: "images/my screenshots.png"

format:
  html:
    code-fold: false
    code-tools: true
    code-copy: true

execute:

  warning: false
---



```{r setup, include=FALSE}
library(reticulate)

# Use Python 3.13
use_python("/Library/Frameworks/Python.framework/Versions/3.13/bin/python3", required = TRUE)
```

使用大型语言模型 (LLM) 对您的数据运行自然语言处理 (NLP) 操作。它利用 LLM 的通用语言训练来获取预测,从而消除了训练新 NLP 模型的需要。mall 可用于 R 和 Python。



# 安装包 (install package)

::: {.panel-tabset group="language"}

## Python
```{python}
#| eval: false
pip install mlverse-mall
pip install chatlas
```

## R
```{python}
#| eval: false
install.packages("mall")
install.packages("ellmer")
```


:::

# 加载包 (load package)

保存 Openrouter API OPENROUTER_API_KEY 到 .env 文件


::: {.panel-tabset group="language"}

## Python


```{python}
import os
import mall
from chatlas import ChatOpenRouter
from dotenv import load_dotenv
import polars as pl
load_dotenv()
```

## R 
```{r}
library(mall)
library(ellmer)
library(dotenv)
library(tidyverse)
# This looks for a .env file in the current working directory
load_dot_env()
```


:::

# 下载数据 (download data)

数据来源: [豆瓣电影 - 飞驰人生3](https://movie.douban.com/subject/37311135/comments?status=P)

::: {.panel-tabset group="language"}

## Python

```{python}
import requests
from bs4 import BeautifulSoup
import time
import random

file_path = "douban_comments.csv"

if not os.path.exists(file_path):
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
        "Referer": "https://movie.douban.com/subject/37311135/",
    }
    
    all_comments = []
    # 抓取前3页,共60条评论
    for page in range(3):
        url = f"https://movie.douban.com/subject/37311135/comments?start={page * 20}&limit=20&status=P&sort=new_score"
        resp = requests.get(url, headers=headers, timeout=10)
        soup = BeautifulSoup(resp.text, "html.parser")
        
        for item in soup.select("div.comment-item"):
            comment_text = item.select_one("span.short")
            rating_span = item.select_one("span.comment-info span.rating")
            user_span = item.select_one("span.comment-info a")
            
            if comment_text:
                # 从 class 如 'allstar50' 提取评分
                rating = ""
                if rating_span:
                    cls = [c for c in rating_span.get("class", []) if c.startswith("allstar")]
                    if cls:
                        rating = str(int(cls[0].replace("allstar", "")) // 10)
                
                all_comments.append({
                    "user": user_span.text.strip() if user_span else "",
                    "rating": rating,
                    "comment": comment_text.text.strip()
                })
        
        time.sleep(random.uniform(2, 4))  # 礼貌延迟
    
    pl.DataFrame(all_comments).write_csv(file_path)
    print(f"已下载 {len(all_comments)} 条评论")
else:
    print(f"文件已存在: {file_path}")

reviews = pl.read_csv(file_path).head(3)
reviews
```

## R

```{r}
library(rvest)

file_path <- "douban_comments.csv"

if (!file.exists(file_path)) {
  all_comments <- data.frame(user = character(), rating = character(), comment = character())
  
  for (page in 0:2) {
    url <- paste0("https://movie.douban.com/subject/37311135/comments?start=", page * 20, "&limit=20&status=P&sort=new_score")
    
    page_html <- read_html(url,
      user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
    )
    
    items <- page_html %>% html_elements("div.comment-item")
    
    for (item in items) {
      comment_text <- item %>% html_element("span.short") %>% html_text2()
      user_name <- item %>% html_element("span.comment-info a") %>% html_text2()
      rating_el <- item %>% html_element("span.comment-info span.rating")
      
      rating <- ""
      if (!is.na(rating_el)) {
        cls <- rating_el %>% html_attr("class")
        rating_num <- gsub(".*allstar(\\d+).*", "\\1", cls)
        rating <- as.character(as.integer(rating_num) %/% 10)
      }
      
      if (!is.na(comment_text)) {
        all_comments <- rbind(all_comments, data.frame(
          user = ifelse(is.na(user_name), "", user_name),
          rating = rating,
          comment = comment_text
        ))
      }
    }
    
    Sys.sleep(runif(1, 2, 4))
  }
  
  write.csv(all_comments, file_path, row.names = FALSE)
  cat("已下载", nrow(all_comments), "条评论\n")
} else {
  cat("文件已存在:", file_path, "\n")
}

reviews <- head(read.csv(file_path), 3)
reviews
```

:::

# 设置模型 (set up model)

::: {.panel-tabset group="language"}



## Python
```{python}
chat = ChatOpenRouter(base_url='https://openrouter.ai/api/v1',
                      api_key=os.getenv("OPENROUTER_API_KEY"),
                      model="minimax/minimax-m2.7"
                      )
```




```{python}
reviews.llm.use(chat)
```

## R

```{r}
chat = chat_openrouter(api_key=Sys.getenv("OPENROUTER_API_KEY"),
                      model="minimax/minimax-m2.7"
                      )


```

```{r}
llm_use(chat)
```




::: 



# 情感分析 (Sentiment)

根据文本自动返回“正面”(positive)、“负面”(negative) 或 “中性”(neutral)。

::: {.panel-tabset group="language"}

## Python
```{python}
reviews.llm.sentiment("comment")
```

## R
```{r}
reviews |>
  llm_sentiment(comment)
```

:::


# 总结 (Summarize)

可能需要减少给定文本中的字数。通常是为了更容易理解其意图。该函数有一个参数来控制输出的最大字数 (max_words):

::: {.panel-tabset group="language"}

## Python
```{python}
reviews.llm.summarize("comment", 10)
```

## R
```{r}
reviews |>
  llm_summarize(comment, max_words = 10)
```
:::


# 分类 (Classify)

使用 LLM 将文本归类到您提供的选项之一:

::: {.panel-tabset group="language"}

## Python
```{python}
reviews.llm.classify("comment", ["推荐", "不推荐"])
```

## R
```{r}
reviews |>
  llm_classify(comment, c("推荐", "不推荐"))
```

::: 


# 提取 (Extract)

最有趣的用例之一,使用自然语言,我们可以告诉 LLM 返回文本的特定部分。在以下示例中,我们请求 LLM 从影评中提取"观影感受"。LLM 会理解该词的含义,并在文本中寻找与之相关的内容。

::: {.panel-tabset group="language"}

## Python
```{python}
reviews.llm.extract("comment", "观影感受")
```

## R
```{r}
reviews |>
  llm_extract(comment, "观影感受")
```

::: 


# 验证 (Verify)


该函数允许您根据提供的文本检查和确认陈述是否为真。默认情况下,它将返回 1 表示“是”,0 表示“否”。这可以被自定义。

::: {.panel-tabset group="language"}

## Python
```{python}
reviews.llm.verify("comment", "是否影评?")
```

## R

```{r}
reviews |>
  llm_verify(comment, "是否影评?")
```

:::


# 翻译 (Translate)

顾名思义,此函数会将文本翻译成指定的语言。非常棒的一点是,您无需指定源文本的语言。只需定义目标语言即可。翻译的准确性将取决于 LLM。

::: {.panel-tabset group="language"}

## Python
```{python}
reviews.llm.translate("comment", "english")
```

## R
```{r}
reviews |>
  llm_translate(comment, "English")
```

::: 


# 自定义提示词 (Custom prompt)

可以向 LLM 传递您自己的提示词,并让 mall 针对每个文本条目运行它:

::: {.panel-tabset group="language"}

## Python
```{python}
my_prompt = (
    "回答一个问题。"
    "只返回答案,不需要解释"
    "可接受的答案是 '值得看', '不值得看'"
    "根据以下影评内容判断,这部电影值不值得看:"
)

reviews.llm.custom("comment", prompt = my_prompt)
```


## R
```{r}
my_prompt <- paste(
  "回答一个问题。",
  "只返回答案,不需要解释",
  "可接受的答案是 '值得看', '不值得看'",
  "根据以下影评内容判断,这部电影值不值得看:"
)

reviews |>
  llm_custom(comment, my_prompt)
```

:::

# 调用 Vector

它也可以接收 Vector 作为输入。


::: {.panel-tabset group="language"}

## Python
```{python}
# Pass it to a new LLMVec
from mall import LLMVec
llm = LLMVec(chat)    
```


```{python}
llm.sentiment(["我很高兴","我不高兴"])
```



## R
```{r}
llm_vec_sentiment(c("我很高兴","我不高兴"))
```

:::


# 参考资料 (Reference)

https://mlverse.github.io/mall/

https://posit-dev.github.io/chatlas/





 
 

This blog is built with ❤️ and Quarto.