Python使用pdfminer.six實現(xiàn)精準控制PDF文本布局
從字符級定位到區(qū)域精確提取,掌握PDF解析的終極武器
前言
在PDF數(shù)據(jù)抽取的江湖中,各路工具各顯神通。但當你面對學術(shù)論文、多列排版雜志、復(fù)雜版式報告這類文檔時,很多工具就敗下陣來——要么提取出的文字順序完全錯亂,要么無法保留原文的層級結(jié)構(gòu)。
這個時候,你需要一把能深入PDF“內(nèi)臟”的手術(shù)刀:pdfminer.six。
它不是那種“開箱即用”的傻瓜式工具,而是一套高度模塊化、可定制的PDF解析框架。它能讓你深入到字符級別的坐標和字體信息,真正做到對文本布局的精準控制。
[圖:pdfminer.six架構(gòu)圖——展示從PDF文件到布局對象的完整解析流程]
本文將帶你徹底吃透pdfminer.six:從安裝配置到層級解析,從過濾頁眉頁腳到按坐標截取特定區(qū)域,最后封裝一套可復(fù)用的工具類,助你從容應(yīng)對各類復(fù)雜版式文檔。
一、pdfminer定位:面向復(fù)雜排版的精細解析工具
1.1 為什么需要pdfminer.six
在Python生態(tài)中,PDF解析工具百花齊放,但各有側(cè)重:
| 工具 | 定位 | 優(yōu)勢 | 劣勢 |
|---|---|---|---|
| pdfplumber | 輕量級全能庫 | API簡潔,表格提取開箱即用 | 基于pdfminer.six封裝,靈活性受限 |
| PyMuPDF | 高性能解析庫 | 速度極快,支持渲染為圖像 | C++后端,布局數(shù)據(jù)獲取相對繁瑣 |
| PyPDF2/pypdf | 基礎(chǔ)PDF操作 | 合并、拆分、加密,輕量級 | 文本提取能力有限[reference:0] |
| pdfminer.six | 底層布局解析 | 字符級位置信息,模塊化可定制 | API復(fù)雜,學習曲線陡峭[reference:1] |
pdfminer.six是原始PDFMiner的社區(qū)維護分支,專注于從PDF源代碼中直接提取頁面文本,并精確獲取文本的位置、字體或顏色信息[reference:2]。它采用模塊化設(shè)計,每個組件都可以輕松替換,你可以實現(xiàn)自己的解釋器或渲染設(shè)備,滿足其他用途[reference:3]。
一句話總結(jié):pdfminer.six給了你PDF解析的“源代碼級別”控制權(quán)。
1.2 pdfminer.six的核心特性
- 純Python實現(xiàn):無需額外依賴,跨平臺支持
- 精確位置獲取:每個字符的坐標、字體、字號都能拿到
- 布局分析:自動重建文本的層級結(jié)構(gòu)(塊→行→字符)
- 多語言支持:中日韓(CJK)語言和豎排文字
- 加密PDF支持:RC4和AES加密
- 表單提取:AcroForm交互式表單
- 目錄提取:PDF書簽/大綱
1.3 適用場景:什么時候非它不可?
- 多列排版學術(shù)論文:普通工具提取后文字順序亂成一鍋粥
- 復(fù)雜版式財報/合同:需要保留原文層級和閱讀順序
- 發(fā)票/收據(jù)關(guān)鍵字段提取:需要按坐標精確定位
- 字體/顏色信息敏感場景:需要保留原始格式元數(shù)據(jù)
- 構(gòu)建定制化解析引擎:pdfminer.six提供了可擴展的組件架構(gòu)
二、安裝依賴與環(huán)境校驗
2.1 基礎(chǔ)安裝
pip install pdfminer.six
驗證安裝是否成功:
python -c "from pdfminer.pdfdocument import PDFDocument; print('安裝成功')"2.2 環(huán)境要求
- Python版本:3.7+
- 核心依賴:pycryptodome(加密模塊)
- 推薦安裝:
pip install pdfminer.six[image](啟用圖像提取支持)
2.3 兩種調(diào)用方式
pdfminer.six提供了兩種層次的使用方式:
方式一:高層API(簡單提?。?/p>
from pdfminer.high_level import extract_text
text = extract_text("sample.pdf")
print(text[:500]) # 打印前500個字符
這是最簡單的用法,適合快速獲取全文文本,但無法獲取位置和布局信息[reference:4]。
方式二:底層API(精細控制)
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextContainer, LTChar
for page_layout in extract_pages("sample.pdf"):
for element in page_layout:
if isinstance(element, LTTextContainer):
print(element.get_text())
# 進一步獲取每個字符的詳細信息
for text_line in element:
for character in text_line:
if isinstance(character, LTChar):
print(f"字符: {character.get_text()}, 字體: {character.fontname}, 字號: {character.size}")
這種方式讓你能夠逐頁、逐元素地遍歷PDF,獲取精確的位置和格式信息[reference:5]。
三、層級解析:頁面→區(qū)塊→行→字符的完整解析鏈路
3.1 布局分析算法原理
pdfminer.six的核心競爭力在于其布局分析(Layout Analysis)系統(tǒng)。這個系統(tǒng)通過啟發(fā)式算法,從PDF中離散的字符位置信息中,重建出有意義的文本結(jié)構(gòu)[reference:6]。
布局分析分為三個階段:
| 階段 | 輸入 | 輸出 | 核心算法 |
|---|---|---|---|
| 階段1 | 單個字符 | 單詞和行 | 基于水平距離和垂直重疊的字符聚類 |
| 階段2 | 文本行 | 文本塊(文本框) | 垂直距離和水平對齊判斷 |
| 階段3 | 文本塊 | 頁面層級結(jié)構(gòu) | 優(yōu)先級隊列的距離聚類 |
3.2 核心對象層級
pdfminer.six將解析后的PDF頁面表示為一個層級化的對象樹,核心類如下:
LTPage(頁面)
├── LTTextBox(文本塊容器)
│ ├── LTTextLine(文本行)
│ │ ├── LTChar(單個字符)
│ │ └── LTChar(單個字符)
│ └── LTTextLine(文本行)
├── LTFigure(圖形容器,可嵌套)
├── LTImage(圖像對象)
└── LTRect(矩形/線條)
3.3 LAParams配置詳解
LAParams(布局分析參數(shù))是控制pdfminer.six行為的核心配置類。以下是最常用的參數(shù):
| 參數(shù) | 默認值 | 作用 |
|---|---|---|
char_margin | 2.0 | 水平方向字符間距容忍度,用于判斷是否屬于同一行 |
word_margin | 0.1 | 單詞間距閾值,超過此值則插入空格 |
line_margin | 0.5 | 垂直方向行間距閾值,用于判斷是否屬于同一段落 |
boxes_flow | 0.5 | 閱讀方向權(quán)重(-1=水平,+1=垂直,None=禁用) |
detect_vertical | False | 是否檢測豎排文字 |
實戰(zhàn)示例:調(diào)整參數(shù)優(yōu)化多列布局解析
from pdfminer.layout import LAParams
from pdfminer.high_level import extract_text
# 針對多列布局優(yōu)化
laparams = LAParams(
char_margin=1.0, # 更嚴格的行內(nèi)字符合并
line_margin=0.3, # 降低行間距容忍度,避免跨列合并
boxes_flow=0.5, # 保持水平閱讀順序
detect_vertical=False
)
text = extract_text("multi_column.pdf", laparams=laparams)
3.4 遍歷頁面元素實戰(zhàn)
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextContainer, LTChar, LTFigure
def traverse_layout(element, level=0):
"""遞歸遍歷PDF布局對象"""
indent = " " * level
# 獲取對象類型和基本信息
obj_type = type(element).__name__
if hasattr(element, "get_text"):
text_preview = element.get_text()[:50].replace("\n", " ")
print(f"{indent}{obj_type}: {text_preview}...")
else:
print(f"{indent}{obj_type}")
# 遞歸遍歷子元素
if hasattr(element, "__iter__") and not isinstance(element, str):
for child in element:
traverse_layout(child, level + 1)
# 使用示例
for page_layout in extract_pages("sample.pdf"):
print(f"\n{'='*50}")
print(f"頁面: {page_layout}")
traverse_layout(page_layout)
四、過濾技巧:剔除頁眉頁腳、水印、注釋等無效內(nèi)容
4.1 基于坐標的區(qū)域過濾
大多數(shù)PDF文檔中,頁眉和頁腳通常位于頁面的頂部和底部。通過坐標判斷,我們可以輕松過濾掉這些無效內(nèi)容[reference:10]。
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextBox, LTFigure
def filter_by_region(element, page_height, top_margin=100, bottom_margin=100):
"""
根據(jù)坐標過濾元素
- top_margin: 頂部排除區(qū)域(像素)
- bottom_margin: 底部排除區(qū)域(像素)
"""
y0 = getattr(element, "y0", 0)
y1 = getattr(element, "y1", page_height)
# 排除頁眉區(qū)域(頂部)
if y1 > page_height - top_margin:
return False
# 排除頁腳區(qū)域(底部)
if y0 < bottom_margin:
return False
return True
# 使用示例
for page_layout in extract_pages("document.pdf"):
page_height = page_layout.height
for element in page_layout:
if isinstance(element, (LTTextBox, LTFigure)):
if filter_by_region(element, page_height, top_margin=80, bottom_margin=80):
print(element.get_text())
4.2 自定義HTMLConverter實現(xiàn)區(qū)域感知提取
pdfminer.six提供了HTMLConverter類,可以生成結(jié)構(gòu)良好的HTML輸出。通過繼承該類,我們可以添加區(qū)域過濾邏輯,同時保留原始文本結(jié)構(gòu)[reference:11]。
from pdfminer.converter import HTMLConverter
from pdfminer.layout import LTTextBox, LTFigure
class RegionAwareHTMLConverter(HTMLConverter):
"""支持區(qū)域過濾的HTML轉(zhuǎn)換器"""
def __init__(self, rsrcmgr, outfp, page_bbox=None, codec='utf-8',
pageno=1, laparams=None, extract_region=None, **kwargs):
super().__init__(rsrcmgr, outfp, codec=codec, pageno=pageno,
laparams=laparams, **kwargs)
self.extract_region = extract_region # (x0, y0, x1, y1)
def _in_region(self, obj):
"""判斷對象是否在目標區(qū)域內(nèi)"""
if self.extract_region is None:
return True
x0, y0, x1, y1 = self.extract_region
return (obj.x0 >= x0 and obj.y0 >= y0 and
obj.x1 <= x1 and obj.y1 <= y1)
def receive_layout(self, ltpage):
"""重寫布局接收方法,添加過濾邏輯"""
for obj in ltpage:
if self._in_region(obj):
if isinstance(obj, LTTextBox):
self.write_text(obj.get_text())
elif isinstance(obj, LTFigure):
self.receive_layout(obj) # 遞歸處理嵌套對象
4.3 過濾水印和注釋
水印和注釋通常具有特定的字體特征(如半透明、特定顏色),可以通過字體名稱和字號來識別。
from pdfminer.layout import LTChar
def filter_watermark(element, watermark_fonts=None):
"""過濾水印"""
if watermark_fonts is None:
watermark_fonts = ['Watermark', 'Stamp']
if hasattr(element, "__iter__"):
for child in element:
filter_watermark(child, watermark_fonts)
elif isinstance(element, LTChar):
if any(font in element.fontname for font in watermark_fonts):
return True # 是水印,跳過
return False
五、精準提?。喊醋鴺私厝≈付▍^(qū)域
5.1 核心思路
PDFMiner.six的extract_pages方法提供了詳細的元素位置信息,但文本被分解為最小單元(LTChar),導(dǎo)致無法直接保持文本結(jié)構(gòu)。通過繼承HTMLConverter,我們可以在保持文本結(jié)構(gòu)的同時,按坐標精確截取特定區(qū)域。
5.2 發(fā)票金額欄提取實戰(zhàn)
假設(shè)我們需要從發(fā)票PDF中提取右上角區(qū)域的金額信息(坐標:x0=500, y0=600, x1=600, y1=700):
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import HTMLConverter
from pdfminer.pdfpage import PDFPage
from pdfminer.layout import LAParams
import io
def extract_region_text(pdf_path, bbox, page_num=0):
"""
提取指定區(qū)域的文本
bbox: (x0, y0, x1, y1) 單位:像素(點)
"""
rsrcmgr = PDFResourceManager()
outfp = io.StringIO()
laparams = LAParams()
# 使用自定義的HTMLConverter
device = RegionAwareHTMLConverter(
rsrcmgr, outfp, laparams=laparams,
extract_region=bbox
)
interpreter = PDFPageInterpreter(rsrcmgr, device)
with open(pdf_path, 'rb') as fp:
for i, page in enumerate(PDFPage.get_pages(fp)):
if i == page_num:
interpreter.process_page(page)
break
device.close()
text = outfp.getvalue()
outfp.close()
return text
# 使用示例:提取發(fā)票金額區(qū)域
bbox = (500, 600, 600, 700) # 根據(jù)實際發(fā)票調(diào)整坐標
amount_text = extract_region_text("invoice.pdf", bbox)
print(f"提取的金額:{amount_text}")
5.3 獲取頁面尺寸與坐標定位
在不知道目標區(qū)域坐標的情況下,可以先遍歷頁面元素,找到目標區(qū)域的參考坐標:
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextBox
def find_element_coordinates(pdf_path, target_keyword):
"""查找包含特定關(guān)鍵詞的元素坐標"""
results = []
for page_layout in extract_pages(pdf_path):
for element in page_layout:
if isinstance(element, LTTextBox):
text = element.get_text()
if target_keyword in text:
results.append({
'text': text,
'bbox': (element.x0, element.y0, element.x1, element.y1),
'page_height': page_layout.height
})
return results
# 使用示例
coords = find_element_coordinates("invoice.pdf", "金額")
for c in coords:
print(f"找到關(guān)鍵詞: {c['text'][:50]}")
print(f"坐標: x0={c['bbox'][0]}, y0={c['bbox'][1]}, x1={c['bbox'][2]}, y1={c['bbox'][3]}")
六、封裝工具類:可復(fù)用的pdfminer解析函數(shù)
為了方便日常使用,我們將pdfminer.six的常用功能封裝成一個工具類:
import io
from typing import List, Dict, Optional, Tuple
from pdfminer.high_level import extract_text, extract_pages
from pdfminer.layout import LAParams, LTTextBox, LTTextLine, LTChar
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import TextConverter, HTMLConverter
class PDFMinerParser:
"""pdfminer.six 封裝工具類"""
def __init__(self, laparams: Optional[LAParams] = None):
self.laparams = laparams or LAParams()
def extract_full_text(self, pdf_path: str) -> str:
"""提取全文"""
return extract_text(pdf_path, laparams=self.laparams)
def extract_metadata(self, pdf_path: str) -> Dict:
"""提取元數(shù)據(jù)"""
from pdfminer.high_level import extract_metadata
metadata = extract_metadata(pdf_path)
return {
'title': getattr(metadata, 'title', ''),
'author': getattr(metadata, 'author', ''),
'subject': getattr(metadata, 'subject', ''),
'creator': getattr(metadata, 'creator', ''),
'producer': getattr(metadata, 'producer', '')
}
def extract_text_with_position(self, pdf_path: str) -> List[Dict]:
"""提取帶位置信息的文本"""
results = []
for page_layout in extract_pages(pdf_path, laparams=self.laparams):
page_height = page_layout.height
for element in page_layout:
if isinstance(element, LTTextBox):
for line in element:
if isinstance(line, LTTextLine):
for char in line:
if isinstance(char, LTChar):
results.append({
'char': char.get_text(),
'x0': char.x0,
'y0': char.y0,
'x1': char.x1,
'y1': char.y1,
'font': char.fontname,
'size': char.size,
'page_height': page_height
})
return results
def extract_region_text(self, pdf_path: str, bbox: Tuple[float, float, float, float],
page_num: int = 0) -> str:
"""提取指定區(qū)域的文本"""
class RegionHTMLConverter(HTMLConverter):
def __init__(self, *args, region=None, **kwargs):
super().__init__(*args, **kwargs)
self.region = region
def receive_layout(self, ltpage):
for obj in ltpage:
if self.region is None:
self.render(obj)
else:
x0, y0, x1, y1 = self.region
if (obj.x0 >= x0 and obj.y0 >= y0 and
obj.x1 <= x1 and obj.y1 <= y1):
self.render(obj)
rsrcmgr = PDFResourceManager()
outfp = io.StringIO()
device = RegionHTMLConverter(rsrcmgr, outfp, laparams=self.laparams,
region=bbox)
interpreter = PDFPageInterpreter(rsrcmgr, device)
with open(pdf_path, 'rb') as fp:
for i, page in enumerate(PDFPage.get_pages(fp)):
if i == page_num:
interpreter.process_page(page)
break
device.close()
result = outfp.getvalue()
outfp.close()
return result
def filter_header_footer(self, pdf_path: str, top_margin: float = 80,
bottom_margin: float = 80) -> str:
"""過濾頁眉頁腳后提取文本"""
class HeaderFooterFilterConverter(TextConverter):
def __init__(self, *args, top_margin=80, bottom_margin=80, **kwargs):
super().__init__(*args, **kwargs)
self.top_margin = top_margin
self.bottom_margin = bottom_margin
def receive_layout(self, ltpage):
for obj in ltpage:
y0 = getattr(obj, 'y0', 0)
y1 = getattr(obj, 'y1', ltpage.height)
if y1 > ltpage.height - self.top_margin:
continue # 跳過頁眉區(qū)域
if y0 < self.bottom_margin:
continue # 跳過頁腳區(qū)域
self.render(obj)
rsrcmgr = PDFResourceManager()
outfp = io.StringIO()
device = HeaderFooterFilterConverter(rsrcmgr, outfp, laparams=self.laparams,
top_margin=top_margin,
bottom_margin=bottom_margin)
interpreter = PDFPageInterpreter(rsrcmgr, device)
with open(pdf_path, 'rb') as fp:
for page in PDFPage.get_pages(fp):
interpreter.process_page(page)
device.close()
result = outfp.getvalue()
outfp.close()
return result
# 使用示例
if __name__ == "__main__":
parser = PDFMinerParser()
# 1. 提取全文
full_text = parser.extract_full_text("sample.pdf")
print(f"全文長度: {len(full_text)} 字符")
# 2. 提取元數(shù)據(jù)
metadata = parser.extract_metadata("sample.pdf")
print(f"作者: {metadata['author']}")
# 3. 提取帶位置的信息
chars = parser.extract_text_with_position("sample.pdf")
print(f"共提取 {len(chars)} 個字符的位置信息")
# 4. 提取指定區(qū)域
region_text = parser.extract_region_text("sample.pdf", (100, 100, 500, 300))
print(f"區(qū)域文本: {region_text[:100]}")
# 5. 過濾頁眉頁腳
clean_text = parser.filter_header_footer("sample.pdf", top_margin=80, bottom_margin=80)
print(f"過濾后文本長度: {len(clean_text)} 字符")
七、適用場景與實戰(zhàn)案例
7.1 多列學術(shù)論文解析
學術(shù)論文通常采用雙欄排版,普通工具提取后文字順序錯亂。pdfminer.six的布局分析可以很好地處理這種場景。
# 針對雙欄論文優(yōu)化參數(shù)
laparams = LAParams(
char_margin=1.0,
line_margin=0.3,
boxes_flow=0.5
)
parser = PDFMinerParser(laparams=laparams)
text = parser.extract_full_text("academic_paper.pdf")
7.2 合同關(guān)鍵條款提取
通過坐標定位,可以精準提取合同中的關(guān)鍵條款字段。
# 定位到合同條款區(qū)域(通常在頁面上半部分)
contract_bbox = (50, 500, 550, 750) # 根據(jù)實際調(diào)整
clause_text = parser.extract_region_text("contract.pdf", contract_bbox)
7.3 批量處理流水線
import os
from concurrent.futures import ThreadPoolExecutor
def batch_parse(pdf_dir: str):
"""批量解析目錄下的所有PDF"""
parser = PDFMinerParser()
results = {}
pdf_files = [f for f in os.listdir(pdf_dir) if f.endswith('.pdf')]
def parse_one(pdf_file):
pdf_path = os.path.join(pdf_dir, pdf_file)
return pdf_file, parser.extract_full_text(pdf_path)
with ThreadPoolExecutor(max_workers=4) as executor:
for pdf_file, text in executor.map(parse_one, pdf_files):
results[pdf_file] = text
return results
# 使用示例
texts = batch_parse("./pdfs/")
for name, content in texts.items():
print(f"{name}: {len(content)} 字符")
八、常見問題與解決方案
Q1:提取的中文出現(xiàn)亂碼?
解決方案:設(shè)置正確的編碼參數(shù),確保字體正確映射。
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfparser import PDFParser
# 使用更底層的API處理中文編碼問題
parser = PDFParser(open('chinese.pdf', 'rb'))
document = PDFDocument(parser)
# 確保CID字體正確加載
Q2:處理大文件時內(nèi)存占用過高?
解決方案:逐頁處理,避免一次性加載全部頁面。
# 使用逐頁迭代而非一次性提取
for page in PDFPage.get_pages(fp, caching=False): # 關(guān)閉緩存
interpreter.process_page(page)
Q3:表格提取效果不理想?
說明:pdfminer.six原生不支持表格識別,建議結(jié)合以下策略:
- 使用坐標定位表格區(qū)域
- 結(jié)合Camelot或pdfplumber專門處理表格
- 基于字符位置信息手動重建表格結(jié)構(gòu)
Q4:掃描件PDF如何處理?
說明:pdfminer.six不包含OCR功能,掃描件PDF需配合OCR工具(如PaddleOCR、Tesseract)使用:
# 先使用PyMuPDF將PDF頁面轉(zhuǎn)為圖像,再用OCR識別
import fitz
doc = fitz.open("scanned.pdf")
for page_num in range(len(doc)):
page = doc[page_num]
pix = page.get_pixmap()
pix.save(f"page_{page_num}.png")
# 然后使用OCR工具識別圖像中的文字
九、總結(jié)
核心要點回顧
| 要點 | 說明 |
|---|---|
| 定位 | 面向復(fù)雜排版的精細解析工具,提供字符級控制 |
| 層級 | LTPage → LTTextBox → LTTextLine → LTChar |
| 核心API | extract_text(快速)、extract_pages(精細) |
| LAParams | 控制布局分析行為的關(guān)鍵參數(shù) |
| 過濾技巧 | 坐標判斷、自定義HTMLConverter |
| 區(qū)域提取 | 繼承HTMLConverter實現(xiàn)精確截取 |
工具選型建議
| 場景 | 推薦工具 |
|---|---|
| 快速提取全文 | pdfplumber / PyMuPDF |
| 表格提取 | Camelot / pdfplumber |
| 復(fù)雜布局/多列排版 | pdfminer.six |
| 字符級位置/字體信息 | pdfminer.six |
| 掃描件OCR | PaddleOCR + pdfminer.six |
| 企業(yè)級高性能 | PyMuPDF |
附錄:速查表
pdfminer.six常用命令
| 命令 | 作用 |
|---|---|
python tools/pdf2txt.py -o output.txt input.pdf | 命令行提取文本 |
python tools/dumppdf.py -a input.pdf | 查看PDF內(nèi)部結(jié)構(gòu) |
核心類速查
| 類名 | 所屬模塊 | 說明 |
|---|---|---|
extract_text | pdfminer.high_level | 高層API:快速提取全文 |
extract_pages | pdfminer.high_level | 高層API:逐頁提取布局對象 |
LAParams | pdfminer.layout | 布局分析參數(shù)配置 |
LTTextBox | pdfminer.layout | 文本塊對象 |
LTTextLine | pdfminer.layout | 文本行對象 |
LTChar | pdfminer.layout | 單個字符對象 |
HTMLConverter | pdfminer.converter | HTML格式輸出轉(zhuǎn)換器 |
TextConverter | pdfminer.converter | 純文本輸出轉(zhuǎn)換器 |
以上就是Python使用pdfminer.six實現(xiàn)精準控制PDF文本布局的詳細內(nèi)容,更多關(guān)于Python pdfminer.six設(shè)置PDF文本布局的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
pandas數(shù)據(jù)預(yù)處理之dataframe的groupby操作方法
下面小編就為大家分享一篇pandas數(shù)據(jù)預(yù)處理之dataframe的groupby操作方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04

