美文网首页Python数据分析
Python 数据处理(三十五)—— 文本数据处理

Python 数据处理(三十五)—— 文本数据处理

作者: 名本无名 | 来源:发表于2021-03-12 19:04 被阅读0次

    1 文本数据类型

    pandas 中,存储文本主要有两种方式

    1. object 类型
    2. StringDtype 扩展类型

    但一般建议使用 StringDtype 类型存储文本数据。都是由于各种原因,现在字符串数据的默认存储类型还是 object

    In [1]: pd.Series(["a", "b", "c"])
    Out[1]: 
    0    a
    1    b
    2    c
    dtype: object
    

    要存储为 string 类型,需要显式的设置 dtype 参数

    In [2]: pd.Series(["a", "b", "c"], dtype="string")
    Out[2]: 
    0    a
    1    b
    2    c
    dtype: string
    
    In [3]: pd.Series(["a", "b", "c"], dtype=pd.StringDtype())
    Out[3]: 
    0    a
    1    b
    2    c
    dtype: string
    

    或者在创建 SeriesDataFrame 之后,使用 astype 转换类型

    In [4]: s = pd.Series(["a", "b", "c"])
    
    In [5]: s
    Out[5]: 
    0    a
    1    b
    2    c
    dtype: object
    
    In [6]: s.astype("string")
    Out[6]: 
    0    a
    1    b
    2    c
    dtype: string
    

    也可以使用 StringDtype/"string" 转换其他非字符串类型的数据

    In [7]: s = pd.Series(["a", 2, np.nan], dtype="string")
    
    In [8]: s
    Out[8]: 
    0       a
    1       2
    2    <NA>
    dtype: string
    
    In [9]: type(s[1])
    Out[9]: str
    

    转换现有数据的类型

    In [10]: s1 = pd.Series([1, 2, np.nan], dtype="Int64")
    
    In [11]: s1
    Out[11]: 
    0       1
    1       2
    2    <NA>
    dtype: Int64
    
    In [12]: s2 = s1.astype("string")
    
    In [13]: s2
    Out[13]: 
    0       1
    1       2
    2    <NA>
    dtype: string
    
    In [14]: type(s2[0])
    Out[14]: str
    
    1.1 行为差异

    StringDtype 类型对象与 object 类型之间存在一些差异

    1. 对于 StringDtype,对于返回数值型输出字符串方法将始终返回非空的 integer 类型。而不是 intfloat 类型。对于布尔型输出方法,返回可空的布尔类型
    In [15]: s = pd.Series(["a", None, "b"], dtype="string")
    
    In [16]: s
    Out[16]: 
    0       a
    1    <NA>
    2       b
    dtype: string
    
    In [17]: s.str.count("a")
    Out[17]: 
    0       1
    1    <NA>
    2       0
    dtype: Int64
    
    In [18]: s.dropna().str.count("a")
    Out[18]: 
    0    1
    2    0
    dtype: Int64
    

    两个结果的输出都是 Int64 类型。将其与 object 类型比较

    In [19]: s2 = pd.Series(["a", None, "b"], dtype="object")
    
    In [20]: s2.str.count("a")
    Out[20]: 
    0    1.0
    1    NaN
    2    0.0
    dtype: float64
    
    In [21]: s2.dropna().str.count("a")
    Out[21]: 
    0    1
    2    0
    dtype: int64
    

    当存在 NA 值时,输出为 float64。类似地,对于返回布尔值的方法

    In [22]: s.str.isdigit()
    Out[22]: 
    0    False
    1     <NA>
    2    False
    dtype: boolean
    
    In [23]: s.str.match("a")
    Out[23]: 
    0     True
    1     <NA>
    2    False
    dtype: boolean
    
    1. 一些字符串方法,如 Series.str.decode()StringArray 上是不可用的。因为 StringArray 只保存字符串,而不是字节

    2. 在比较操作中,arrays.StringArrayStringArray 支持的 Series 将返回一个具有 BooleanDtype 的对象,而不是一个 bool 对象。StringArray 中的缺失值会在比较操作中传播,而不是像 numpy.nan 那样总是比较不等

    2 字符串方法

    SeriesIndex 有一套字符串处理方法,可以方便地对数组的每个元素进行操作,最重要的是,这些方法会自动忽略缺失值。

    这些方法可以通过 str 属性访问,通常具有与内置字符串方法相匹配的名称

    In [24]: s = pd.Series(
       ....:     ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
       ....: )
       ....: 
    
    In [25]: s.str.lower()
    Out[25]: 
    0       a
    1       b
    2       c
    3    aaba
    4    baca
    5    <NA>
    6    caba
    7     dog
    8     cat
    dtype: string
    
    In [26]: s.str.upper()
    Out[26]: 
    0       A
    1       B
    2       C
    3    AABA
    4    BACA
    5    <NA>
    6    CABA
    7     DOG
    8     CAT
    dtype: string
    
    In [27]: s.str.len()
    Out[27]: 
    0       1
    1       1
    2       1
    3       4
    4       4
    5    <NA>
    6       4
    7       3
    8       3
    dtype: Int64
    
    In [28]: idx = pd.Index([" jack", "jill ", " jesse ", "frank"])
    
    In [29]: idx.str.strip()
    Out[29]: Index(['jack', 'jill', 'jesse', 'frank'], dtype='object')
    
    In [30]: idx.str.lstrip()
    Out[30]: Index(['jack', 'jill ', 'jesse ', 'frank'], dtype='object')
    
    In [31]: idx.str.rstrip()
    Out[31]: Index([' jack', 'jill', ' jesse', 'frank'], dtype='object')
    

    Index 上的字符串方法对于清理或转换 DataFrame 的列特别有用。

    例如,您可能有带有前导或后置空格的列

    In [32]: df = pd.DataFrame(
       ....:     np.random.randn(3, 2), columns=[" Column A ", " Column B "], index=range(3)
       ....: )
       ....: 
    
    In [33]: df
    Out[33]: 
        Column A    Column B 
    0    0.469112   -0.282863
    1   -1.509059   -1.135632
    2    1.212112   -0.173215
    

    因为 df.columns 是一个 Index 对象,所以我们可以使用 .str 访问器

    In [34]: df.columns.str.strip()
    Out[34]: Index(['Column A', 'Column B'], dtype='object')
    
    In [35]: df.columns.str.lower()
    Out[35]: Index([' column a ', ' column b '], dtype='object')
    

    我们可以根据需要对列名进行处理,然后重新设置列名。

    例如,我们删除列名的前后空格,并将其改为小写字母,同时用 _ 替换剩余的空格

    In [36]: df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
    
    In [37]: df
    Out[37]: 
       column_a  column_b
    0  0.469112 -0.282863
    1 -1.509059 -1.135632
    2  1.212112 -0.173215
    

    3 切割和替换字符串

    split 方法会返回一个值为 listSeries

    In [38]: s2 = pd.Series(["a_b_c", "c_d_e", np.nan, "f_g_h"], dtype="string")
    
    In [39]: s2.str.split("_")
    Out[39]: 
    0    [a, b, c]
    1    [c, d, e]
    2         <NA>
    3    [f, g, h]
    dtype: object
    

    可以使用 get[] 访问拆分后的列表中的元素

    In [40]: s2.str.split("_").str.get(1)
    Out[40]: 
    0       b
    1       d
    2    <NA>
    3       g
    dtype: object
    
    In [41]: s2.str.split("_").str[1]
    Out[41]: 
    0       b
    1       d
    2    <NA>
    3       g
    dtype: object
    

    更简单的方法是设置 expand 参数,返回一个 DataFrame

    In [42]: s2.str.split("_", expand=True)
    Out[42]: 
          0     1     2
    0     a     b     c
    1     c     d     e
    2  <NA>  <NA>  <NA>
    3     f     g     h
    

    当原来的 Series 包含 StringDtype 类型的数据时,输出列也将全部为 StringDtype

    当然,也可以设置切割次数

    In [43]: s2.str.split("_", expand=True, n=1)
    Out[43]: 
          0     1
    0     a   b_c
    1     c   d_e
    2  <NA>  <NA>
    3     f   g_h
    

    它还有个对应的 rsplit 方法,从右边起始对字符串进行拆分

    In [44]: s2.str.rsplit("_", expand=True, n=1)
    Out[44]: 
          0     1
    0   a_b     c
    1   c_d     e
    2  <NA>  <NA>
    3   f_g     h
    

    replace 参数支持使用正则表达式,前两个参数是 pat(匹配模式) 和 repl(替换字符串)

    In [45]: s3 = pd.Series(
       ....:     ["A", "B", "C", "Aaba", "Baca", "", np.nan, "CABA", "dog", "cat"],
       ....:     dtype="string",
       ....: )
       ....: 
    
    In [46]: s3
    Out[46]: 
    0       A
    1       B
    2       C
    3    Aaba
    4    Baca
    5        
    6    <NA>
    7    CABA
    8     dog
    9     cat
    dtype: string
    
    In [47]: s3.str.replace("^.a|dog", "XX-XX ", case=False, regex=True)
    Out[47]: 
    0           A
    1           B
    2           C
    3    XX-XX ba
    4    XX-XX ca
    5            
    6        <NA>
    7    XX-XX BA
    8      XX-XX 
    9     XX-XX t
    dtype: string
    

    如果只是想要替换字符串字面值,可以将 regex 参数设置为 False,而不需要对每个特殊字符进行转义。此时 patrepl 参数必须是字符串

    In [48]: dollars = pd.Series(["12", "-$10", "$10,000"], dtype="string")
    
    # These lines are equivalent
    In [49]: dollars.str.replace(r"-\$", "-", regex=True)
    Out[49]: 
    0         12
    1        -10
    2    $10,000
    dtype: string
    
    In [50]: dollars.str.replace("-$", "-", regex=False)
    Out[50]: 
    0         12
    1        -10
    2    $10,000
    dtype: string
    

    此外,replace 方法还接受一个可调用的替换函数,会使用 re.sub() 方法在每个匹配的模式上调用该函数

    该函数需要传入一个正则对象作为位置参数,并返回一个字符串。例如

    # 反转每个小写单词的顺序
    In [51]: pat = r"[a-z]+"
    
    In [52]: def repl(m):
       ....:     return m.group(0)[::-1]
       ....: 
    
    In [53]: pd.Series(["foo 123", "bar baz", np.nan], dtype="string").str.replace(
       ....:     pat, repl, regex=True
       ....: )
       ....: 
    Out[53]: 
    0    oof 123
    1    rab zab
    2       <NA>
    dtype: string
    
    # 使用 regex 捕获分组
    In [54]: pat = r"(?P<one>\w+) (?P<two>\w+) (?P<three>\w+)"
    
    In [55]: def repl(m):
       ....:     return m.group("two").swapcase()
       ....: 
    
    In [56]: pd.Series(["Foo Bar Baz", np.nan], dtype="string").str.replace(
       ....:     pat, repl, regex=True
       ....: )
       ....: 
    Out[56]: 
    0     bAR
    1    <NA>
    dtype: string
    

    replace 方法的 pat 参数还接受 re.compile() 编译的正则表达式对象。所有的 flags 需要在编译正则对象时设置

    In [57]: import re
    
    In [58]: regex_pat = re.compile(r"^.a|dog", flags=re.IGNORECASE)
    
    In [59]: s3.str.replace(regex_pat, "XX-XX ", regex=True)
    Out[59]: 
    0           A
    1           B
    2           C
    3    XX-XX ba
    4    XX-XX ca
    5            
    6        <NA>
    7    XX-XX BA
    8      XX-XX 
    9     XX-XX t
    dtype: string
    

    如果在 replace 中设置 flags 参数,则会抛出异常

    In [60]: s3.str.replace(regex_pat, 'XX-XX ', flags=re.IGNORECASE)
    ---------------------------------------------------------------------------
    ValueError: case and flags cannot be set when pat is a compiled regex
    

    4 连接

    有几种方法可以将一个 SeriesIndex 与自己或其他的 SeriesIndex 相连接,所有这些方法都是基于 cat() 方法

    4.1 将单个 Series 对象连接成字符串

    可以连接一个 SeriesIndex 的内容

    In [61]: s = pd.Series(["a", "b", "c", "d"], dtype="string")
    
    In [62]: s.str.cat(sep=",")
    Out[62]: 'a,b,c,d'
    

    如果未指定 sep 参数,则默认为空字符串

    In [63]: s.str.cat()
    Out[63]: 'abcd'
    

    默认会跳过缺失值,也可以使用 na_rep 指定缺失值的表示方式

    In [64]: t = pd.Series(["a", "b", np.nan, "d"], dtype="string")
    
    In [65]: t.str.cat(sep=",")
    Out[65]: 'a,b,d'
    
    In [66]: t.str.cat(sep=",", na_rep="-")
    Out[66]: 'a,b,-,d'
    
    4.2 连接 Series 与一个类似列表的对象

    cat() 的第一个参数 others 可以是类似列表的对象,但是其长度需要和调用对象一致

    In [67]: s.str.cat(["A", "B", "C", "D"])
    Out[67]: 
    0    aA
    1    bB
    2    cC
    3    dD
    dtype: string
    

    只要两个对象中存在缺失值,对应的结果中也是缺失值,除非指定了 na_rep

    In [68]: s.str.cat(t)
    Out[68]: 
    0      aa
    1      bb
    2    <NA>
    3      dd
    dtype: string
    
    In [69]: s.str.cat(t, na_rep="-")
    Out[69]: 
    0    aa
    1    bb
    2    c-
    3    dd
    dtype: string
    
    4.3 连接 Series 与多个类似列表的对象

    others 参数也可以是二维的,但是得保证其行数必须与调用的对象一致

    In [70]: d = pd.concat([t, s], axis=1)
    
    In [71]: s
    Out[71]: 
    0    a
    1    b
    2    c
    3    d
    dtype: string
    
    In [72]: d
    Out[72]: 
          0  1
    0     a  a
    1     b  b
    2  <NA>  c
    3     d  d
    
    In [73]: s.str.cat(d, na_rep="-")
    Out[73]: 
    0    aaa
    1    bbb
    2    c-c
    3    ddd
    dtype: string
    
    4.4 连接一个 Series 和一个带索引的对象

    对于 SeriesDataFrame 的连接,可以通过设置 join 参数指定对齐方式

    In [74]: u = pd.Series(["b", "d", "a", "c"], index=[1, 3, 0, 2], dtype="string")
    
    In [75]: s
    Out[75]: 
    0    a
    1    b
    2    c
    3    d
    dtype: string
    
    In [76]: u
    Out[76]: 
    1    b
    3    d
    0    a
    2    c
    dtype: string
    
    In [77]: s.str.cat(u)
    Out[77]: 
    0    aa
    1    bb
    2    cc
    3    dd
    dtype: string
    
    In [78]: s.str.cat(u, join="left")
    Out[78]: 
    0    aa
    1    bb
    2    cc
    3    dd
    dtype: string
    

    通常 join 可选范围为: 'left', 'outer', 'inner', 'right'。此时,不再要求两个对象长度一致

    In [79]: v = pd.Series(["z", "a", "b", "d", "e"], index=[-1, 0, 1, 3, 4], dtype="string")
    
    In [80]: s
    Out[80]: 
    0    a
    1    b
    2    c
    3    d
    dtype: string
    
    In [81]: v
    Out[81]: 
    -1    z
     0    a
     1    b
     3    d
     4    e
    dtype: string
    
    In [82]: s.str.cat(v, join="left", na_rep="-")
    Out[82]: 
    0    aa
    1    bb
    2    c-
    3    dd
    dtype: string
    
    In [83]: s.str.cat(v, join="outer", na_rep="-")
    Out[83]: 
    -1    -z
     0    aa
     1    bb
     2    c-
     3    dd
     4    -e
    dtype: string
    

    others 参数是 DataFrame 时,也可以使用

    In [84]: f = d.loc[[3, 2, 1, 0], :]
    
    In [85]: s
    Out[85]: 
    0    a
    1    b
    2    c
    3    d
    dtype: string
    
    In [86]: f
    Out[86]: 
          0  1
    3     d  d
    2  <NA>  c
    1     b  b
    0     a  a
    
    In [87]: s.str.cat(f, join="left", na_rep="-")
    Out[87]: 
    0    aaa
    1    bbb
    2    c-c
    3    ddd
    dtype: string
    
    4.5 连接 Series 和多个对象

    可以将一些类似数组的对象(如 SeriesIndex 等)放在一个类似列表的容器中,然后传递给 cat

    In [88]: s
    Out[88]: 
    0    a
    1    b
    2    c
    3    d
    dtype: string
    
    In [89]: u
    Out[89]: 
    1    b
    3    d
    0    a
    2    c
    dtype: string
    
    In [90]: s.str.cat([u, u.to_numpy()], join="left")
    Out[90]: 
    0    aab
    1    bbd
    2    cca
    3    ddc
    dtype: string
    

    对于没有索引的对象,其长度必须与调用 cat 的对象相同。但是 SeriesIndex 可以是任意的,除非设置了 json=None

    In [91]: v
    Out[91]: 
    -1    z
     0    a
     1    b
     3    d
     4    e
    dtype: string
    
    In [92]: s.str.cat([v, u, u.to_numpy()], join="outer", na_rep="-")
    Out[92]: 
    -1    -z--
     0    aaab
     1    bbbd
     2    c-ca
     3    dddc
     4    -e--
    dtype: string
    

    如果在 others 参数上包含不同索引的对象,且设置了 join='right',则最后的结果将会是这些索引的并集

    In [93]: u.loc[[3]]
    Out[93]: 
    3    d
    dtype: string
    
    In [94]: v.loc[[-1, 0]]
    Out[94]: 
    -1    z
     0    a
    dtype: string
    
    In [95]: s.str.cat([u.loc[[3]], v.loc[[-1, 0]]], join="right", na_rep="-")
    Out[95]: 
    -1    --z
     0    a-a
     3    dd-
    dtype: string
    

    相关文章

      网友评论

        本文标题:Python 数据处理(三十五)—— 文本数据处理

        本文链接:https://www.haomeiwen.com/subject/yigzqltx.html