pandas 在NaN行拆分数据框

c9qzyr3d  于 2023-09-29  发布在  其他
关注(0)|答案(3)|浏览(169)

已经有一个答案,它处理一个相对简单的 Dataframe ,给定here
然而,我手头的这个框架有多个列和大量的行。一个DataFrame包含三个沿着axis=0连接的DataFrame。(一个的底端连接到下一个的顶部。)它们由一行NaN值分隔。
我如何通过沿着NaN行拆分一个数据来创建三个 Dataframe ?

uz75evzq

uz75evzq1#

与您链接的答案一样,您希望创建一个标识组编号的列。然后,您可以使用相同的解决方案。
为此,您必须测试一行的所有值是否为NaN。我不知道pandas中是否有这样一个内置的测试,但是pandas有一个测试来检查一个Series是否充满了NaN。所以你要做的是对 Dataframe 的转置执行,这样你的“Series”实际上就是你的行:

  1. df["group_no"] = df.isnull().all(axis=1).cumsum()

在这一点上,你可以使用相同的技术,从该答案分裂的边框。
您可能希望在最后执行.dropna(),因为结果中仍然有NaN行。

j8ag8udp

j8ag8udp2#

在2022年遇到了同样的问题。下面是我用NaN在行上分割行的方法,需要注意的是,这依赖于pip install python-rle的游程编码:

  1. import rle
  2. def nanchucks(df):
  3. # It chucks NaNs outta dataframes
  4. # True if whole row is NaN
  5. df_nans = pd.isnull(df).sum(axis="columns").astype(bool)
  6. values, counts = rle.encode(df_nans)
  7. df_nans = pd.DataFrame({"values": values, "counts": counts})
  8. df_nans["cum_counts"] = df_nans["counts"].cumsum()
  9. df_nans["start_idx"] = df_nans["cum_counts"].shift(1)
  10. df_nans.loc[0, "start_idx"] = 0
  11. df_nans["start_idx"] = df_nans["start_idx"].astype(int) # np.nan makes it a float column
  12. df_nans["end_idx"] = df_nans["cum_counts"] - 1
  13. # Only keep the chunks of data w/o NaNs
  14. df_nans = df_nans[df_nans["values"] == False]
  15. indices = []
  16. for idx, row in df_nans.iterrows():
  17. indices.append((row["start_idx"], row["end_idx"]))
  18. return [df.loc[df.index[i[0]]: df.index[i[1]]] for i in indices]

示例如下:

  1. sample_df1 = pd.DataFrame({
  2. "a": [1, 2, np.nan, 3, 4],
  3. "b": [1, 2, np.nan, 3, 4],
  4. "c": [1, 2, np.nan, 3, 4],
  5. })
  6. sample_df2 = pd.DataFrame({
  7. "a": [1, 2, np.nan, 3, 4],
  8. "b": [1, 2, 3, np.nan, 4],
  9. "c": [1, 2, np.nan, 3, 4],
  10. })
  11. print(nanchucks(sample_df1))
  12. # [ a b c
  13. # 0 1.0 1.0 1.0
  14. # 1 2.0 2.0 2.0,
  15. # a b c
  16. # 3 3.0 3.0 3.0
  17. # 4 4.0 4.0 4.0]
  18. print(nanchucks(sample_df2))
  19. # [ a b c
  20. # 0 1.0 1.0 1.0
  21. # 1 2.0 2.0 2.0,
  22. # a b c
  23. # 4 4.0 4.0 4.0]
展开查看全部
dzjeubhm

dzjeubhm3#

使用NaNs改进其他支持多行的答案:

  1. from IPython.display import display
  2. import pandas as pd
  3. def split_df_if_row_full_nans(df, reset_header=False):
  4. # grouping
  5. df = (df
  6. .assign(_nan_all_cols=df.isnull().all(axis=1))
  7. .assign(_group_no=lambda df_: df_._nan_all_cols.cumsum())
  8. .query('_nan_all_cols == False') # Drop rows where _nan_all_cols is True
  9. .drop(columns=['_nan_all_cols']) # Drop the _nan_all_cols column
  10. .reset_index(drop=True)
  11. )
  12. # splitting
  13. dfs = {df.iloc[rows[0],0]: (df
  14. .iloc[rows]
  15. .drop(columns=['_group_no'])
  16. )
  17. for _, rows in df.groupby('_group_no').groups.items()}
  18. if reset_header:
  19. # rename column and set index
  20. for k, v in dfs.items():
  21. dfs[k] = (v
  22. .rename(columns=v.iloc[0])
  23. .drop(index=v.index[0])
  24. )
  25. # TODO: this part seems to only works if length of the df is > 1
  26. # dfs[k].set_index(dfs[k].columns[0], drop=True, inplace=True)
  27. # # display
  28. # for df in dfs.values():
  29. # display(df)
  30. return dfs
  31. sample_df1 = pd.DataFrame({
  32. "a": [1, 2, np.nan, 3, 4],
  33. "b": [1, 2, np.nan, 3, 4],
  34. "c": [1, 2, np.nan, 3, 4],
  35. })
  36. sample_df2 = pd.DataFrame({
  37. "a": [1, 2, np.nan, 3, 4],
  38. "b": [1, 2, 3, np.nan, 4],
  39. "c": [1, 2, np.nan, 3, 4],
  40. })
  41. for df in split_df_if_row_full_nans(sample_df1).values():
  42. display(df)
  43. # 1.0 1.0 1.0
  44. # 1 2 2 2
  45. # 3.0 3.0 3.0
  46. # 3 4 4 4
  47. for df in split_df_if_row_full_nans(sample_df2).values():
  48. display(df)
  49. # 1.0 1.0 1.0
  50. # 1 2 2 2
  51. # 2 NaN 3 NaN
  52. # 3 3 NaN 3
  53. # 4 4 4 4

注意:此方法使用.isnull().all(axis=1),即仅当所有值都是NaN时才进行拆分。

展开查看全部

相关问题