zoukankan      html  css  js  c++  java
  • pandas中的那些让人有点懵逼的异常(坑向)

    楔子

    pandas是一个很强大的库,但是在使用的过程中难免会遇见各种奇葩的异常,而这些异常却又很难让人定位到底是哪一步出了问题。下面就来看看pandas中的一些令人感到费解的异常吧,看看你有没有遇到过,如果没有的话,那么说明你pandas可能用的不够多哦。

    ヽ( ̄ω ̄( ̄ω ̄〃)ゝ一起来看看

    1. SettingWithCopyWarning:

    当然这不是个异常,而是一个警告,这个警告相信大多数人都遇到过,尤其是初学pandas的时候。这个警告具体内容如下:

    SettingWithCopyWarning: 
    A value is trying to be set on a copy of a slice from a DataFrame.
    Try using .loc[row_indexer,col_indexer] = value instead
    
    See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/......
    

    我们来复现一下这个警告:

    import pandas as pd
    
    df = pd.DataFrame({"name": ["mashiro", "koishi", "satori", "kurisu"],
                       "age": [17, 16, 17, 19],
                       "adult": [None, None, None, None]})
    
    print(df)
    """
          name  age adult
    0  mashiro   17  None
    1   koishi   16  None
    2   satori   17  None
    3   kurisu   19  None
    """
    # 我现在想将df中age > 18对应的audit设置为True
    
    # 但是会发现没有效果,并且SettingWithCopyWarning就是由这一行代码引发的
    df[df["age"] > 16]["adult"] = True
    print(df)
    """
          name  age adult
    0  mashiro   17  None
    1   koishi   16  None
    2   satori   17  None
    3   kurisu   19  None
    """
    

    为什么会出现这个原因呢?因为df[df["age"] > 16]得到的是原始DataFrame的一份拷贝,因此其相应的操作不会影响原来的DataFrame。尽管这样的操作是允许的,但是却无法得到正确的结果,因此pandas弹出了一个警告。

    # 真正的做法是使用loc或者iloc
    df.loc[df["age"] > 18, "adult"] = True
    print(df)
    """
          name  age adult
    0  mashiro   17  None
    1   koishi   16  None
    2   satori   17  None
    3   kurisu   19  None
    """
    

    2. TypeError: 'Series' objects are mutable, thus they cannot be hashed

    这个异常实际上比较常见了,说白了就是你不小心把loc或者iloc给丢掉了,我们还用上面的例子

    try:
        df[df["age"] > 18, "adult"] = True
    except Exception as e:
        print(e)  # 'Series' objects are mutable, thus they cannot be hashed
    

    3. ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()

    我们知道,像if a:,not a之类的,本质上都是调用a的__bool__方法,可以理解为bool(a)

    class A:
    
        def __bool__(self):
            return True
    
    
    if A():
        print("123")
    else:
        print("456")
    print(bool(A()))
    """
    123
    True
    """
    
    
    # 由于A的__bool__返回了True, 所以bool(A())为True
    # 我们将其改为False
    A.__bool__ = lambda self: False
    print(bool(A()))  # False
    

    但是对于一个Series或者numpy中的array不可以这么做。

    import pandas as pd
    
    s = pd.Series([True, False])
    try:
        bool(s)
    except Exception as e:
        print(e)  # The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
    
    try:
        if s.values:  # 得到一个numpy中的array
            pass
    except Exception as e:
        print(e)  # The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
    
    
    """
    对于一个Series对象来说,不可以使用其布尔值,应当使用 s.all()、s.any()或者np.all(s)、np.any(s)
    
    但是对于numpy中的array来讲,如果这个array里面的元素不止一个,那么也不可以使用其布尔值,但如果该array中只有一个元素的话是个例外
    此时:if np.array([123]) 等价于 if 123
    
    因此如果使用其布尔值的话,最好使用np.all()或者np.any()将一个序列变成单个布尔值
    """
    
    # 但如果只是希望像列表一样,如果该Series对象里面有值就是真,否则就是假
    # 那么建议通过 if len(s):这种方式来判断
    # 同理DataFrame也是如此
    

    4. 布尔你咋啦?

    我们知道,可以对两个类型为bool的Series对象进行 与、或、非 等操作,但是结果真的一定是我们想要的吗?

    import pandas as pd
    
    s1 = pd.Series([True, True, True])
    s2 = pd.Series([True, False, True], index=[1, 2, 3])
    
    print(s1 & s2)
    """
    0    False
    1     True
    2    False
    3    False
    dtype: bool
    """
    # 我们看到与运算之后,长度变成了4,究其原因就是两个Series索引不同造成的
    # 而Series的很多操作都是基于索引进行对齐的,并不是简简单单地按照顺序
    # 但如果对应的索引一样的话,那么也可以认为就是按照顺序从上到下
    # 但如果索引不一样的话,pandas是怎么做的呢?答案是使用reindex
    
    # 首先找到两个Series对象中出现的所有不重复索引
    index = s1.index | s2.index
    print(index)  # Int64Index([0, 1, 2, 3], dtype='int64')
    
    # 使用reindex进行对齐, 不存在的使用NaN代替,当然我们也可以指定fill_value进行填充
    # 比如fill_value=False
    print(s1.reindex(index))
    """
    0    True
    1    True
    2    True
    3     NaN
    dtype: object
    """
    print(s2.reindex(index))
    """
    0      NaN
    1     True
    2    False
    3     True
    dtype: object
    """
    
    # 所以s1 & s2最终等价于 s1.reindex(index) & s2.reindex(index)
    # 因此即使两者个数不同也是没有问题的
    s1 = pd.Series([True, True, True, True])
    s2 = pd.Series([True, True, True], index=[1, 2, 3])
    print(s1 & s2)
    """
    0    False
    1     True
    2     True
    3     True
    dtype: bool
    """
    

    总之pandas中很多操作,并不是我们想的那么简单,pandas的Series和DataFrame都具备索引的概念,通过索引来定位速度是非常快的。但是不注意就会造成陷阱,究其原因就是很多操作在定位的时候是基于索引来定位的,并不是简单的按照顺序。比如:s1 & s2,指的是s1和s2中相同索引对应的元素进行与运算,当然如果有对应不上的,事先已经通过reindex处理好了。

    当然,如果我们不希望考虑索引的话,只是单纯的希望按照顺序进行位运算,该怎么做呢?办法有两种

    import pandas as pd
    
    s1 = pd.Series([True, True, True])
    s2 = pd.Series([True, False, True], index=[1, 2, 3])
    
    # 对Series使用reset_index即可,当然要指定drop=True,否则就变成DataFrame了
    # 一旦reset_index之后两者索引从头到尾就是一致的了
    print(s1.reset_index(drop=True) & s2.reset_index(drop=True))
    """
    0     True
    1    False
    2     True
    dtype: bool
    """
    
    # 或者转成numpy中的array
    # 我们知道Series等价于numpy中的n个array,分别存放索引、值等等
    # 我们调用s.index即可拿到索引,s.values即可拿到值
    print(s1.values & s2)
    """
    1     True
    2    False
    3     True
    dtype: bool
    """
    # 如果其中是一个array的话,那么它没有索引的概念,索引此时也是单纯的一个一个对应进行运算
    # 当然得到的结果也是一个Series,索引和运算的Series的索引保持一致
    
    # 或者都转成array
    print(s1.values & s2.values)  # [ True False  True]
    
    # 但是注意:如果其中一方转成了array,那么此时就要求两个序列的布尔元素个数是必须相等的
    # 此时就不会再通过reindex进行扩展了,因为array没有reindex
    # 当然都转成array就更不用说了
    

    我们说了很多关于索引的话题,之所以强调这一点,是因为这里面存在一个坑点。我们知道对于DataFrame对象来说,通过df[xxx]可以取得相应的数据,xxx的身份不同,取得的数据也不同

    • 如果xxx是一个标量, 那么df[xxx]表示获取df的某一列,得到一个Series对象
    • 如果xxx是一个列表或者numpy的narray, 那么xxx里面可以是该DataFrame对象的列名,表示获取指定的多个列,得到DataFrame对象
    • 如果xxx是一个列表或者numpy的narray,那么这个xxx里面还可以是布尔值,并且其长度要和该DataFrame对象的行数相等,表示获取对应的行数。对应为True的保留,为False的不要,也是得到DataFrame对象

    举个栗子

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
    
    
    # 只有第一个为True,因此被保留了下来
    print(df[[True, False, False]])
    """
       a   b
    0  1  11
    """
    
    try:
        # 但此时指定的4个布尔值
        print(df[[True, False, False, False]])
    except Exception as e:
        print(e)  # Item wrong length 4 instead of 3.
    """
    告诉我们个数不匹配
    所以上面之所以说了索引,就是因为在做运算的的时候可能导致布尔值的个数最终和DataFrame的行数不匹配
    从而在筛选指定记录的时候发生报错
    """
    

    如果是Series也是可以的

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
    
    flag = pd.Series([True, False, False])
    print(df[flag])
    """
       a   b
    0  1  11
    """
    # 当然里面也可以是一个Series,当然这个Series不仅个数要匹配,索引也要匹配
    # 我们知道df的索引是 0 1 2,那么该flag的索引也必须是0 1 2(但是顺序不要求)
    flag.index = [1, 2, 3]
    try:
        df[flag]
    except Exception as e:
        print(e)
    # Unalignable boolean Series provided as indexer (index of the boolean Series and of the indexed object do not match).
    
    
    """
    我们看到报了上面那个错误,意思我们传递了bool类型的Series对象,但是其索引和DataFrame的索引不匹配
    """
    
    # 我们再改一下
    flag.index = [1, 2, 0]
    print(flag)
    """
    1     True
    2    False
    0    False
    dtype: bool
    """
    print(df[flag])
    """
       a   b
    1  2  22
    """
    # 我们看到此时布尔值True对应的索引为1,那么筛选的就不再是df中的第一行了
    # 而是索引为1的行,也就是第二行。
    # 因此尽管对Series的索引的值有要求,但是对顺序却并没有要求
    # 所以这种情况下,筛选出来的数据可能就和我们想象的不一样,明明第一个是True,为啥却把DataFrame的第二行选出来了
    # 原因就是,虽然第一个是True,但是它对应的索引是1
    

    因此索引这个东西在定位数据的时候,会非常方便,因为我们可以直接通过索引去定位。但是在一些操作方面,我们关心的并不是它的索引,而是它的值,比如:s1 & s2,或者df[flag],这个时候我们只是对内部的布尔值感兴趣,那么直接把s1、s2、flag这些变成numpy中的array之后,再去传递即可。此时就无需考虑索引啥的了。

    5. 怎么给DataFrame添加字段呢?

    给一个DataFrame添加一个字段,并附上初始值有以下几种方式。

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
    
    # 可以给一个标量,然后会自动进行广播
    df["c"] = "xx"
    
    # 也可以是一个列表,如果里面只有一个元素,那么和标量是等价的
    df["d"] = "yy"
    print(df)
    """
       a   b   c   d
    0  1  11  xx  yy
    1  2  22  xx  yy
    2  3  33  xx  yy
    """
    
    # 如果是列表里面有多个值,那么个数必须和df的行数匹配
    # 否则会报出ValueError: Length of values does not match length of index
    df["e"] = ["x", "y", "z"]
    print(df)
    """
       a   b   c   d  e
    0  1  11  xx  yy  x
    1  2  22  xx  yy  y
    2  3  33  xx  yy  z
    """
    
    # 还有一种办法是通过df.assign,这种办法可以同时创建多个列
    df = df[["a", "b"]]
    df = df.assign(
        # 这里指定接收一个参数的函数,这个参数就是整个df
        # 通过关键字参数,那么参数名就是列名
        c=lambda x: x["a"] + 1,
        d=lambda x: x["b"] * 2,
        e=lambda x: ["i", "j", "k"],
        f=lambda x: "哼哼"
    )
    print(df)
    """
       a   b  c   d  e   f
    0  1  11  2  22  i  哼哼
    1  2  22  3  44  j  哼哼
    2  3  33  4  66  k  哼哼
    """
    

    给一个DataFrame添加一个字段,同样存在索引的陷阱

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
    
    df["c"] = pd.Series(["x", "y", "z"], index=[0, 2, 3])
    print(df)
    """
       a   b    c
    0  1  11    x
    1  2  22  NaN
    2  3  33    y
    """
    

    原因无需我再多解释,总而言之就是我们刚才说的那样,如果我们只关心值,不关心索引,那么就不要传递Series对象,直接传递numpy中的array或者列表即可,这样我们就根本不需要考虑索引对齐的问题。

    传递一个一维序列是可以的,那么传递一个DataFrame对象会如何呢?

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
    df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
    
    try:
        # 因为df1有两个字段,这里我们只指定了一个
        df["c"] = df1
    except Exception as e:
        print(e)  # Wrong number of items passed 2, placement implies 1
    
    # 我们知道df1["x"]是个Series,所以df["c"] = df1["x"]肯定没有错
    # 但是df["c"] = df1[["x"]]呢?  df1[["x"]]显然是个DataFrame
    df["c"] = df1[["x"]]
    print(df)
    """
       a   b     c
    0  1  11    aa
    1  2  22    22
    2  3  33  None
    """
    # 可以看到,如果DataFrame只有一个字段,那么等价于Series
    
    
    # 最后,df["xx"] = xx 这种方式, 在xx是一维序列的前提下 完全等价于 df.loc[:, "xx"] = xx
    # 但如果xx是一个DataFrame的话就不一样了
    df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
    df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
    
    df.loc[:, "c"] = df1
    print(df)
    """
       a   b   c
    0  1  11 NaN
    1  2  22 NaN
    2  3  33 NaN
    """
    # 我们惊奇地发现它居然没有报错,但结果却是NaN
    # 我们rename一下
    df1 = df1.rename(columns={"y": "c"})
    df.loc[:, "c"] = df1
    print(df)
    """
       a   b   c
    0  1  11  >>
    1  2  22  ^^
    2  3  33  YY
    """
    # 因此我们发现在使用df.loc[:, "xx"] = df1的时候
    # 会自动去找df1中列名为"xx"的列,如果找不到就为NaN
    

    如果给DataFrame添加多个字段的话,除了assign之外,还有什么办法呢?

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
    df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
    
    df[["c", "d"]] = df1
    print(df)
    """
       a   b     c   d
    0  1  11    aa  >>
    1  2  22    22  ^^
    2  3  33  None  YY
    """
    # 这里可以要求列名不一致,但是个数必须要匹配
    
    
    df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
    df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
    # 但是对于loc来说,无法添加多个字段
    # 添加一个字段是可以的,但是多个不行
    try:
        df.loc[:, ["c", "d"]] = df1
    except Exception as e:
        # loc表示筛选,而df的列中没有"c"和"d"
        # 即使是df.loc[:, ["c"]]也不可以,但是df.loc[:, "c"]是可以的
        print(e)  # "None of [Index(['c', 'd'], dtype='object')] are in the [columns]"
    
    # 所以df[["c", "d"]] = df1,如果列c、d不存在, 那么会自动添加
    # 但是对于df.loc[:, ["c", "d"]] = df1,如果c、d不存在,则报错,注意:不是都不存在,而是只要有一个不存在就报错
    # 如果是指定了不存在的索引,暂时不会报错,而是弹出一个警告
    print(df.loc[[1, 11]])
    """
          a     b
    1   2.0  22.0
    11  NaN   NaN
    """
    # 我们看到指定了不存在的索引,那么自动为NaN
    # 但同时会抛出一个FutureWarning:
    """
    Passing list-likes to .loc or [] with any missing label will raise
    KeyError in the future, you can use .reindex() as an alternative.
    """
    # 意思是让我们先reindex一下
    

    所以如果想添加多个字段,可以直接通过df[["c1", "c2"]] = df1的方式,但是注意:右边写的df1,所以右边需要也是一个DataFrame,并且两者列数相等

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
    df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
    
    # 这里我们本意是想创建两个字段,值都为None
    # 但是很遗憾这样不可以
    try:
        # 右边的值也需要是一个DataFrame
        df[["c", "d"]] = None
    except Exception as e:
        # 我们看到这里也同样报出了相应的错误
        # 因此只有当右边的值是DataFrame的时候,df[["c", "d"]]才能具备创建新字段的能力
        print(e)  # "None of [Index(['c', 'd'], dtype='object')] are in the [columns]"
    
    # 如果想创建多个新字段,并且还希望通过广播的方式赋上同一个值,那么上面做法是行不通的
    # 解决办法是一个字段一个字段的创建,这样百分之百是没有任何问题的,既可以df["c"]也可以df.loc[:, "c"]
    # 但是也可以通过我们之前说的assign
    df = df.assign(
        c=lambda x: None,
        d=lambda x: None,
    )
    print(df)
    """
       a   b     c     d
    0  1  11  None  None
    1  2  22  None  None
    2  3  33  None  None
    """
    
    # 除此之外,还有一个insert方法
    # 这个方法接收:插入的位置、列名、值
    # 比如我想在列c的后面插入一个新列age,值全部是18,该怎么做呢?
    df.insert(df.columns.get_loc("c") + 1, "age", 18)
    print(df)
    """
       a   b     c  age     d
    0  1  11  None   18  None
    1  2  22  None   18  None
    2  3  33  None   18  None
    """
    # 我们看到insert这个方法是在本地进行操作的
    # 关键是第一个参数,我们希望插在c的后面,那么就必须获取c所在的索引,当然也可以直接数出来
    # 通过columns.get_loc即可获取,然后再加上1即可
    

    6. ValueError: cannot compute isin with a duplicate axis.

    这个错误当初也是把我搞懵逼了半天,在复现这个异常之前,我们先来聊聊非常常用的isin

    isin我们一般是对Series对象使用,判断这个序列中每一个元素是不是在另一个序列里面,下面举例说明:

    import pandas as pd
    
    s = pd.Series(["a", "b", "c", "d"])
    print(s.isin(["a", "c", "e"]))
    """
    0     True
    1    False
    2     True
    3    False
    dtype: bool
    """
    

    这个方法是我们经常使用的,但是你对DataFrame使用过isin吗?我们有时候需要判断两个序列,看这两个序列中的值是否在另外两个序列里面。

    import pandas as pd
    
    df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
    df2 = pd.DataFrame({"x": ["a", "B", "c", "D"], "y": [1, 2, 4, 3]})
    print(df1)
    """
       x  y
    0  a  1
    1  b  2
    2  c  3
    3  d  4
    """
    print(df2)
    """
       x  y
    0  a  1
    1  B  2
    2  c  4
    3  D  3
    """
    print(df1[["x"]].isin(df2))
    # DataFrame中有两列,所以是两列布尔值
    """
           x      y
    0   True   True
    1  False   True
    2   True  False
    3  False  False
    """
    # 我们来分析一下,对于df1来说,前两行肯定是没有问题的
    # 但是第三行有点诡异,我们df1的第三行的y列是3,显然3是在df2的y列当中啊,为什么是False
    # 同理第4行,"d"不在df2的x列中我们知道,但是y列的4很明显在df2的y列当中,为什么是False
    

    估计有人猜到了,那就是对DataFrame使用isin的时候,多个列之间并不是独立的。事实上,DataFrame使用isin也是根据索引来的,我们举个栗子

    import pandas as pd
    
    df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
    df2 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
    # 两个一模一样的DataFrame对象
    print(df1.isin(df2))
    """
          x     y
    0  True  True
    1  True  True
    2  True  True
    3  True  True
    """
    # 结果没问题,但是我们将df2的索引改变一下
    df2.index = [0, 1, 3, 2]
    print(df1.isin(df2))
    """
           x      y
    0   True   True
    1   True   True
    2  False  False
    3  False  False
    """
    # 此时我们就看到端倪了,对于DataFrame对象来讲,isin是判断对应索引的字段的值是否相同
    

    但是问题又来了,因为这样显然不是我们期望的结果。因为即使df2中存在,但如果索引对不上的话也没有任何意义,因此我们可以手动设置索引。

    import pandas as pd
    
    df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
    df2 = pd.DataFrame({"x": ["a", "b", "d", "c"], "y": [1, 2, 4, 3]})
    
    # 我们将x和y设置为索引不就行了,加上drop=False表示设置索引的同时,还作为列
    df1 = df1.set_index(["x", "y"], drop=False)
    df2 = df2.set_index(["x", "y"], drop=False)
    print(df1)
    """
         x  y
    x y      
    a 1  a  1
    b 2  b  2
    c 3  c  3
    d 4  d  4
    """
    print(df2)
    """
         x  y
    x y      
    a 1  a  1
    b 2  b  2
    d 4  d  4
    c 3  c  3
    """
    
    print(df1.isin(df2))
    """
            x     y
    x y            
    a 1  True  True
    b 2  True  True
    c 3  True  True
    d 4  True  True
    """
    # 在通过all(axis=1)即可找到满足条件的值
    print(df1.isin(df2).all(axis=1).values)  # [ True  True  True  True]
    
    # 我们看到此时根据索引去找,就能够准确的定位了
    # 不过细心的人可能已经发现了,这个索引是由x和y两列得到的,事实上索引如果匹配上了,那么值一定是相等的
    # 所以此时就没必要在进行对比了
    # 是的,所以我们可以换一种方法
    df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
    df2 = pd.DataFrame({"x": ["a", "b", "d", "c"], "y": [1, 2, 4, 3]})
    
    # pandas中有一个Index类,Series和DataFrame的索引就是Index类型
    # 当然Index分为好几种,但是它们继承自Index
    print(type(df1.index))  # <class 'pandas.core.indexes.range.RangeIndex'>
    # 根据x和y两列创建Index对象
    index1 = pd.Index(df1[["x", "y"]])
    index2 = pd.Index(df2[["x", "y"]])
    print(index1)  # Index([('a', 1), ('b', 2), ('c', 3), ('d', 4)], dtype='object')
    print(index2)  # Index([('a', 1), ('b', 2), ('d', 4), ('c', 3)], dtype='object')
    
    # Index对象可以像集合一样,取并集、交集,当然此时我们可以直接使用isin
    # 因为它们整体变成了一个元组,也就是说,此时是一个一维序列,对于一维序列可以直接使用isin
    # 直接返回一个numpy中的array
    print(index1.isin(index2))  # [ True  True  True  True]
    

    然而这么做有一个弊端,没错,我要复现我们上面的异常了

    import pandas as pd
    
    df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
    df2 = pd.DataFrame({"x": ["a", "a", "c", "d"], "y": [1, 1, 3, 4]})
    
    # 我们将x和y设置为索引不就行了,加上drop=False表示设置索引的同时,还作为列
    df1 = df1.set_index(["x", "y"], drop=False)
    df2 = df2.set_index(["x", "y"], drop=False)
    
    try:
        print(df1.isin(df2))
    except Exception as e:
        print(e)  # cannot compute isin with a duplicate axis.
    
    # 我们对一个DataFrame使用isin,那么要求isin里面的DataFrame的索引是不可以重复的,否则就会报出上面这个错误
    # 解决办法是使用pd.Index
    print(pd.Index(df1[["x", "y"]]).isin(pd.Index(df2[["x", "y"]])))  # [ True False  True  True]
    
    # 然鹅,我记得pd.Index这种做法也不保险
    # 由于索引的特殊性,好像这种情况我记得也报错,但是目前没有
    # 因此最稳妥的办法是再转成Series
    s1 = pd.Series(pd.Index(df1[["x", "y"]]))
    s2 = pd.Series(pd.Index(df2[["x", "y"]]))
    
    print(s1)
    """
    0    (a, 1)
    1    (b, 2)
    2    (c, 3)
    3    (d, 4)
    dtype: object
    """
    print(s2)
    """
    0    (a, 1)
    1    (a, 1)
    2    (c, 3)
    3    (d, 4)
    dtype: object
    """
    print(s1.isin(s2))
    """
    0     True
    1    False
    2     True
    3     True
    dtype: bool
    """
    # 这种做法是百分之百没有问题的
    
    
    # 忘记说了,df1.isin(df2)的时候,两个列的名称一定要对应
    df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
    df2 = pd.DataFrame({"a": ["a", "a", "c", "d"], "y": [1, 1, 3, 4]})
    print(df1.isin(df2))
    """
           x      y
    0  False   True
    1  False  False
    2  False   True
    3  False   True
    """
    # 由于df2中没有x这一列,因此相当于NaN,所以结果为False
    print(df1[["y"]].isin(df2))
    """
           y
    0   True
    1  False
    2   True
    3   True
    """
    # 会自动找df2中名称为y的列进行比较,因此记得注意列名
    # 当然由于df1.isin(df2)在索引方面的局限性,我们一般也不会使用这种方法
    # 而是会将DataFrame的每一个字段的值拼接成一个元组,整体得到一个Series对象
    # 然后对Series对象使用isin,这是最正确的做法
    

    7. ValueError: cannot set a frame with no defined index and a scalar

    这个错误不是很常见,我们来看一下。

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 1, 1, 1], "b": [1, 1, 1, 1]})
    
    df.loc[df["a"] > 2, "c"] = 1
    print(df)
    """
       a  b   c
    0  1  1 NaN
    1  1  1 NaN
    2  1  1 NaN
    3  1  1 NaN
    """
    

    我们将df["a"] > 2的记录选出来,然后同时创建"c"这一列,并设置对应的记录为1。如果不满足条件,那么会自动为NaN,而我们没有满足条件的记录,所以全部为NaN

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 1, 1, 1], "b": [1, 1, 1, 1]})
    
    df.loc[:, "c"] = 1
    print(df)
    """
       a  b  c
    0  1  1  1
    1  1  1  1
    2  1  1  1
    3  1  1  1
    """
    

    上面这种赋值方式也是可以的,我们之前说,对于一维序列,df["xx"]等价于df.loc[:, "xx"],但实际上还是有点区别的,那就是后者要求DataFrame不可以为空

    import pandas as pd
    
    df = pd.DataFrame({"a": [], "b": []})
    print(df)
    """
    Empty DataFrame
    Columns: [a, b]
    Index: []
    """
    
    try:
        df.loc[:, "c"] = 1
    except Exception as e:
        print(e)  # cannot set a frame with no defined index and a scalar
    
    # 空DataFrame的话,只能用df["c"] = 1的方式
    df["c"] = 1
    print(df)
    """
    Empty DataFrame
    Columns: [a, b, c]
    Index: []
    """
    

    8. ValueError: If using all scalar values, you must pass an index

    这个错误应该遇见的比较少,我们看看这种错误是怎么发生的。

    import pandas as pd
    
    # 我们说通过字典构建DataFrame,value应该是序列,不应该是一个标量
    try:
        df = pd.DataFrame({"a": 123, "b": None})
    except Exception as e:
        print(e)  # If using all scalar values, you must pass an index
    
    # 如果传递标量的话,那么应该同时指定一个index, index是只有一个元素的列表,里面是一个索引
    df = pd.DataFrame({"a": 123, "b": None}, index=["索引"])
    print(df)
    """
          a     b
    索引  123  None
    """
    

    有待发掘。。。。

  • 相关阅读:
    C# memoryStream
    C# memoryStream
    C# HSSFWorkbook与XSSFWorkbook的区别和.xls和.xlsx的区别
    C# HSSFWorkbook与XSSFWorkbook的区别和.xls和.xlsx的区别
    C# winform 启动外部程序
    C# winform 启动外部程序
    NPOI2.2.0.0实例详解(九)—设置EXCEL单元格【时间格式】
    NPOI2.2.0.0实例详解(九)—设置EXCEL单元格【时间格式】
    FileStream 的FileShare一点小认识
    FileStream 的FileShare一点小认识
  • 原文地址:https://www.cnblogs.com/traditional/p/13090698.html
Copyright © 2011-2022 走看看