【问题标题】:Converting Pandas DataFrame to Orange Table将 Pandas DataFrame 转换为橙色表
【发布时间】:2014-10-12 00:29:38
【问题描述】:

我注意到这是一个issue on GitHub already。有人有将 Pandas DataFrame 转换为 Orange Table 的代码吗?

明确地说,我有下表。

       user  hotel  star_rating  user  home_continent  gender
0         1     39          4.0     1               2  female
1         1     44          3.0     1               2  female
2         2     63          4.5     2               3  female
3         2      2          2.0     2               3  female
4         3     26          4.0     3               1    male
5         3     37          5.0     3               1    male
6         3     63          4.5     3               1    male

【问题讨论】:

  • 橙色的格式看起来并不难输出:docs.orange.biolab.si/reference/rst/Orange.data.formats.html 还支持导入csv文件和猜测数据类型,你试过吗?
  • 所以我可以理解如何将数据保存到 *.tab 文件中,但具体来说,是否有一个函数或一系列调用可以让您将 panda DataFrame 转换为 Orange Table? (旁注:有趣的是,该页面如何谈论数据如何存储在外部文件中,但没有谈论如何从文件中保存/加载。我个人认为 Orange 没有很好的文档记录。)
  • 将 Pandas 中的表保存为文件然后在 Orange 中导入文件的工作流程是否可行?还是太杂了?我猜可能无法很好地传递字段数据类型。
  • @BKay 这是一个开始,但我正在寻找更优雅或更直接的东西。从本质上讲,这听起来像是 EdChum 的想法。

标签: python pandas dataframe orange


【解决方案1】:

为了将 pandas DataFrame 转换为 Orange Table,您需要构建一个域,该域指定列类型。

对于连续变量,您只需要提供变量的名称,但对于离散变量,您还需要提供所有可能值的列表。

以下代码将为您的 DataFrame 构建一个域并将其转换为橙色表:

import numpy as np
from Orange.feature import Discrete, Continuous
from Orange.data import Domain, Table
domain = Domain([
    Discrete('user', values=[str(v) for v in np.unique(df.user)]),
    Discrete('hotel', values=[str(v) for v in np.unique(df.hotel)]),
    Continuous('star_rating'),
    Discrete('user', values=[str(v) for v in np.unique(df.user)]),
    Discrete('home_continent', values=[str(v) for v in np.unique(df.home_continent)]),
    Discrete('gender', values=['male', 'female'])], False)
table = Table(domain, [map(str, row) for row in df.as_matrix()])

需要 map(str, row) 步骤,以便 Orange 知道数据包含离散特征的值(而不是值列表中值的索引)。

【讨论】:

  • 这很好用!我对其进行了测试,似乎我可以按性别对表格进行排序,所以我假设大多数其他表格函数都可以工作。
  • 如果您想将一个特征描述为一个 ID,是否没有其他数据类型? (例如,用户 ID)
【解决方案2】:

这样的?

table = Orange.data.Table(df.as_matrix())

Orange 中的列将获得通用名称(a1、a2...)。如果要从数据框中复制名称和类型,请从数据框中构造 Orange.data.Domain 对象 (http://docs.orange.biolab.si/reference/rst/Orange.data.domain.html#Orange.data.Domain.init) 并将其作为上面的第一个参数传递。

请参阅http://docs.orange.biolab.si/reference/rst/Orange.data.table.html 中的构造函数。

【讨论】:

  • 尝试此操作时出现域错误。 “TypeError:构造函数的无效参数(域或示例或两者都预期)”。你能提供一些代码来添加到域中吗?
  • 说你有df = DataFrame({"A": [1, 2, 3, 4], "B": [8, 7, 6, 5]})。用domain = Orange.data.Domain([Orange.feature.Continuous(name) for name in df.columns])table = Orange.data.Table(domain, df.as_matrix()) 构造一个域
  • 哦,如果它不起作用:你的数据框是什么样的?如果df.as_matrix().dtypeobject,Orange 不会接受。您必须将分类数据转换为索引。
【解决方案3】:
from Orange.data.pandas_compat import table_from_frame,table_to_frame
df= table_to_frame(in_data)
#here you go
out_data = table_from_frame(df)

基于 Creo 的回答

【讨论】:

    【解决方案4】:

    下面来自a closed issue on github的回答

    from Orange.data.pandas_compat import table_from_frame
    out_data = table_from_frame(df)
    

    df 是你的数据框。到目前为止,我只注意到如果数据源不是 100% 干净且不符合所需的 ISO 标准,则需要手动定义一个域来处理日期。

    我意识到这是一个老问题,与第一次被问到时相比发生了很大变化 - 但这个问题在谷歌搜索结果中出现在该主题的顶部。

    【讨论】:

    • 感谢@Creo,点赞。简单的解决方案,并于 2020 年 8 月 4 日在 Python 3.7.6 上运行
    • 我一直在发疯,试图弄清楚这一点,这正是我正在寻找的解决方案,谢谢@Creo
    【解决方案5】:

    Orange 包的文档并未涵盖所有细节。根据lib_kernel.cppTable._init__(Domain, numpy.ndarray) 仅适用于 intfloat

    他们确实应该为pandas.DataFrames 提供C 级接口,或者至少为numpy.dtype("str") 提供支持。

    更新:添加 table2dfdf2table 通过将 numpy 用于 int 和 float 大大提高了性能。

    将这段脚本保存在你的橙色 python 脚本集合中,现在你的橙色环境中已经配备了 pandas。

    用法a_pandas_dataframe = table2df( a_orange_table )a_orange_table = df2table( a_pandas_dataframe )

    注意:此脚本仅适用于 Python 2.x,有关 Python 3.x 兼容脚本,请参阅 @DustinTang 的 answer

    import pandas as pd
    import numpy as np
    import Orange
    
    #### For those who are familiar with pandas
    #### Correspondence:
    ####    value <-> Orange.data.Value
    ####        NaN <-> ["?", "~", "."] # Don't know, Don't care, Other
    ####    dtype <-> Orange.feature.Descriptor
    ####        category, int <-> Orange.feature.Discrete # category: > pandas 0.15
    ####        int, float <-> Orange.feature.Continuous # Continuous = core.FloatVariable
    ####                                                 # refer to feature/__init__.py
    ####        str <-> Orange.feature.String
    ####        object <-> Orange.feature.Python
    ####    DataFrame.dtypes <-> Orange.data.Domain
    ####    DataFrame.DataFrame <-> Orange.data.Table = Orange.orange.ExampleTable 
    ####                              # You will need this if you are reading sources
    
    def series2descriptor(d, discrete=False):
        if d.dtype is np.dtype("float"):
            return Orange.feature.Continuous(str(d.name))
        elif d.dtype is np.dtype("int"):
            return Orange.feature.Continuous(str(d.name), number_of_decimals=0)
        else:
            t = d.unique()
            if discrete or len(t) < len(d) / 2:
                t.sort()
                return Orange.feature.Discrete(str(d.name), values=list(t.astype("str")))
            else:
                return Orange.feature.String(str(d.name))
    
    
    def df2domain(df):
        featurelist = [series2descriptor(df.icol(col)) for col in xrange(len(df.columns))]
        return Orange.data.Domain(featurelist)
    
    
    def df2table(df):
        # It seems they are using native python object/lists internally for Orange.data types (?)
        # And I didn't find a constructor suitable for pandas.DataFrame since it may carry
        # multiple dtypes
        #  --> the best approximate is Orange.data.Table.__init__(domain, numpy.ndarray),
        #  --> but the dtype of numpy array can only be "int" and "float"
        #  -->  * refer to src/orange/lib_kernel.cpp 3059:
        #  -->  *    if (((*vi)->varType != TValue::INTVAR) && ((*vi)->varType != TValue::FLOATVAR))
        #  --> Documents never mentioned >_<
        # So we use numpy constructor for those int/float columns, python list constructor for other
    
        tdomain = df2domain(df)
        ttables = [series2table(df.icol(i), tdomain[i]) for i in xrange(len(df.columns))]
        return Orange.data.Table(ttables)
    
        # For performance concerns, here are my results
        # dtndarray = np.random.rand(100000, 100)
        # dtlist = list(dtndarray)
        # tdomain = Orange.data.Domain([Orange.feature.Continuous("var" + str(i)) for i in xrange(100)])
        # tinsts = [Orange.data.Instance(tdomain, list(dtlist[i]) )for i in xrange(len(dtlist))] 
        # t = Orange.data.Table(tdomain, tinsts)
        #
        # timeit list(dtndarray)  # 45.6ms
        # timeit [Orange.data.Instance(tdomain, list(dtlist[i])) for i in xrange(len(dtlist))] # 3.28s
        # timeit Orange.data.Table(tdomain, tinsts) # 280ms
    
        # timeit Orange.data.Table(tdomain, dtndarray) # 380ms
        #
        # As illustrated above, utilizing constructor with ndarray can greatly improve performance
        # So one may conceive better converter based on these results
    
    
    def series2table(series, variable):
        if series.dtype is np.dtype("int") or series.dtype is np.dtype("float"):
            # Use numpy
            # Table._init__(Domain, numpy.ndarray)
            return Orange.data.Table(Orange.data.Domain(variable), series.values[:, np.newaxis])
        else:
            # Build instance list
            # Table.__init__(Domain, list_of_instances)
            tdomain = Orange.data.Domain(variable)
            tinsts = [Orange.data.Instance(tdomain, [i]) for i in series]
            return Orange.data.Table(tdomain, tinsts)
            # 5x performance
    
    
    def column2df(col):
        if type(col.domain[0]) is Orange.feature.Continuous:
            return (col.domain[0].name, pd.Series(col.to_numpy()[0].flatten()))
        else:
            tmp = pd.Series(np.array(list(col)).flatten())  # type(tmp) -> np.array( dtype=list (Orange.data.Value) )
            tmp = tmp.apply(lambda x: str(x[0]))
            return (col.domain[0].name, tmp)
    
    def table2df(tab):
        # Orange.data.Table().to_numpy() cannot handle strings
        # So we must build the array column by column,
        # When it comes to strings, python list is used
        series = [column2df(tab.select(i)) for i in xrange(len(tab.domain))]
        series_name = [i[0] for i in series]  # To keep the order of variables unchanged
        series_data = dict(series)
        print series_data
        return pd.DataFrame(series_data, columns=series_name)
    

    【讨论】:

    • 看来您提供了非常全面的回复,谢谢!这些函数是否适用于每个 Orange 表/Panda DataFrame?
    • 希望是的,我在自己的数据集上进行了测试,但可能需要进行更多测试。
    • 这对我在 Python3 和 Orange3 中不起作用。不过,谢谢!
    • @DustinTang 的回答(见下文)适用于 Python 3.5 和 Orange 3.10
    • 感谢@pedrovgp 的提醒。我已经修改了答案。
    【解决方案6】:

    这段代码是从@TurtleIzzy for Python3 修改而来的。

    import numpy as np
    from Orange.data import Table, Domain, ContinuousVariable, DiscreteVariable
    
    
    def series2descriptor(d):
        if d.dtype is np.dtype("float") or d.dtype is np.dtype("int"):
            return ContinuousVariable(str(d.name))
        else:
            t = d.unique()
            t.sort()
            return DiscreteVariable(str(d.name), list(t.astype("str")))
    
    def df2domain(df):
        featurelist = [series2descriptor(df.iloc[:,col]) for col in range(len(df.columns))]
        return Domain(featurelist)
    
    def df2table(df):
        tdomain = df2domain(df)
        ttables = [series2table(df.iloc[:,i], tdomain[i]) for i in range(len(df.columns))]
        ttables = np.array(ttables).reshape((len(df.columns),-1)).transpose()
        return Table(tdomain , ttables)
    
    def series2table(series, variable):
        if series.dtype is np.dtype("int") or series.dtype is np.dtype("float"):
            series = series.values[:, np.newaxis]
            return Table(series)
        else:
            series = series.astype('category').cat.codes.reshape((-1,1))
            return Table(series)
    

    【讨论】:

      【解决方案7】:

      table_from_frame 在 Python 3 中可用,不允许定义类列,因此生成的表不能直接用于训练分类模型。我调整了 table_from_frame 函数,以便它允许定义类列。请注意,类名应作为附加参数给出。

      """Pandas DataFrame↔Table conversion helpers"""
      import numpy as np
      import pandas as pd
      from pandas.api.types import (
          is_categorical_dtype, is_object_dtype,
          is_datetime64_any_dtype, is_numeric_dtype,
      )
      
      from Orange.data import (
          Table, Domain, DiscreteVariable, StringVariable, TimeVariable,
          ContinuousVariable,
      )
      
      __all__ = ['table_from_frame', 'table_to_frame']
      
      
      def table_from_frame(df,class_name, *, force_nominal=False):
          """
          Convert pandas.DataFrame to Orange.data.Table
      
          Parameters
          ----------
          df : pandas.DataFrame
          force_nominal : boolean
              If True, interpret ALL string columns as nominal (DiscreteVariable).
      
          Returns
          -------
          Table
          """
      
          def _is_discrete(s):
              return (is_categorical_dtype(s) or
                      is_object_dtype(s) and (force_nominal or
                                              s.nunique() < s.size**.666))
      
          def _is_datetime(s):
              if is_datetime64_any_dtype(s):
                  return True
              try:
                  if is_object_dtype(s):
                      pd.to_datetime(s, infer_datetime_format=True)
                      return True
              except Exception:  # pylint: disable=broad-except
                  pass
              return False
      
          # If df index is not a simple RangeIndex (or similar), put it into data
          if not (df.index.is_integer() and (df.index.is_monotonic_increasing or
                                             df.index.is_monotonic_decreasing)):
              df = df.reset_index()
      
          attrs, metas,calss_vars = [], [],[]
          X, M = [], []
      
          # Iter over columns
          for name, s in df.items():
              name = str(name)
              if name == class_name:
                  discrete = s.astype('category').cat
                  calss_vars.append(DiscreteVariable(name, discrete.categories.astype(str).tolist()))
                  X.append(discrete.codes.replace(-1, np.nan).values)
              elif _is_discrete(s):
                  discrete = s.astype('category').cat
                  attrs.append(DiscreteVariable(name, discrete.categories.astype(str).tolist()))
                  X.append(discrete.codes.replace(-1, np.nan).values)
              elif _is_datetime(s):
                  tvar = TimeVariable(name)
                  attrs.append(tvar)
                  s = pd.to_datetime(s, infer_datetime_format=True)
                  X.append(s.astype('str').replace('NaT', np.nan).map(tvar.parse).values)
              elif is_numeric_dtype(s):
                  attrs.append(ContinuousVariable(name))
                  X.append(s.values)
              else:
                  metas.append(StringVariable(name))
                  M.append(s.values.astype(object))
      
          return Table.from_numpy(Domain(attrs, calss_vars, metas),
                                  np.column_stack(X) if X else np.empty((df.shape[0], 0)),
                                  None,
                                  np.column_stack(M) if M else None)
      

      【讨论】:

        【解决方案8】:

        这很好用

        from Orange.data.pandas_compat import table_from_frame,table_to_frame
        
        import pandas as pd
        
        
        # read the input data into pandas data-frame 
        df= table_to_frame(in_data)
        
        # perform all data operations / wrangling 
        
        # for example only few columns are required in output 
        df = df[['Col1', 'Col2']]
        
        
        
        # Final output 
        out_data = table_from_frame(df)
        

        【讨论】:

          猜你喜欢
          • 2012-10-07
          • 1970-01-01
          • 2017-03-17
          • 2017-03-23
          • 2017-04-13
          • 2021-03-29
          • 2017-04-27
          • 2017-01-28
          相关资源
          最近更新 更多