【Pandas 数据分析 3】Pandas 数据读取与输出

7 篇文章 0 订阅
7 篇文章 0 订阅

目录

3.1 数据读取

3.1.1 CSV文件

3.1.2 Excel

3.1.3 JSON

3.1.4 HTML

3.1.5 剪贴板

3.1.6 SQL

3.2 读取CSV

3.2.1 语法

3.2.2 数据内容

3.2.3 分隔符

3.2.4 表头

3.2.5 列名

3.2.6 索引

3.2.7 使用部分列

3.2.8 返回序列

3.2.9 表头前缀

3.2.10 处理重复列名

3.2.11 数据类型

3.2.12 引擎

3.2.13 列数据处理

3.2.14 真假值转换

3.2.15 跳过指定行

3.2.16 读取指定行

3.2.17 空值替换

3.2.18 保留默认空值

3.2.19 日期时间解析

3.2.20 文件处理

3.2.21 符号


3.1 数据读取

从常见的Excel和CSV到JSON及各种数据库,Pandas几乎支持市面上所有的主流数据存储形式。

Pandas提供了一组顶层的I/O API,如pandas.read_csv()等方法,这些方法可以将众多格式的数据读取到DataFrame数据结构中,经过分析处理后,再通过类似DataFrame.to_csv()的方法导出数据。

Pandas 中常见数据的读取和输出函数

 输入和输出的方法如下:

        读取函数一般会赋值给一个变量df,df = pd.read_<xxx>();

        输出函数是将变量自身进行操作并输出df.to_<xxx>();

3.1.1 CSV文件

CSV(Comma-Separated Values)是用逗号分隔值的数据形式,有时也称为字符分隔值,因为分隔字符也可以不是逗号。CSV文件的一般文件扩展名为.csv,用制表符号分隔也常用.tsv作为扩展名。CSV不仅可以是一个实体文件,还可以是字符形式,以便于在网络上传输。

CSV文件的读取方法如下(以下代码省略了赋值操作):

# 文件目录
pd.read_csv('data.csv') # 如果文件与代码文件在同一目录下
pd.read_csv('data/my/data.csv') # 指定目录
pd.read_csv('data/my/my.data') # CSV文件的扩展名不一定是.csv

CSV文件可以存储在网络上,通过URL来访问和读取:

# 使用URL
pd.read_csv('https://www.gairuo.com/file/data/dataset/GDP-China.csv')

CSV不带数据样式,标准化较强,是最为常见的数据格式。

3.1.2 Excel

Excel电子表格是微软公司开发的被广泛使用的电子数据表格软件,一般可以将它的使用分为两类。一类是文字或者信息的结构化,像排班表、工作日报、客户名单之类,以文字为主;另一类为统计报表,如学生成绩表、销售表等,以数字为核心。Pandas主要处理统计报表,当然也可以对文字信息类表格做整理,在新版本的Pandas中加入了非常强大的文本处理功能。

Excel虽然易于上手,功能也很强大,但在数据分析中缺点也很明显。

        无法进行复杂的处理:有时Excel提供的函数和处理方法无法满足复杂逻辑。

        无法支持更大的数据量:目前Excel支持的行数上限为1 048 576(2的20次方),列数上限为16 384(2的14次方,列标签为XFD),在数据分析、机器学习操作中往往会超过这个体量。

        处理方法无法复用:Excel一般采用设定格式的公式,然后将数据再复制,但这样仍然无法对数据的处理过程进行灵活复用。无法自动化:数据分析要经过一个数据输入、处理、分析和输出的过程,这些都是由人工来进行操作,无法实现自动化。

Pandas可以读取、处理大体量的数据,通过技术手段,理论上Pandas可以处理的数据体量无限大。编程可以更加自由地实现复杂的逻辑,逻辑代码可以进行封装、重复使用并可实现自动化。Pandas也提供了非常丰富的读取操作,这些将在3.2节详细介绍。最基础的读取方法如下:

# 返回DataFrame
pd.read_excel('team.xlsx') # 默认读取第一个标签页Sheet
pd.read_excel('path_to_file.xlsx', sheet_name='Sheet1') # 指定Sheet
# 从URL读取
pd.read_excel('https://www.gairuo.com/file/data/dataset/team.xlsx')

3.1.3 JSON

JSON是互联网上非常通用的轻量级数据交换格式,是HTTP请求中数据的标准格式之一。Pandas提供的JSON读取方法在解析网络爬虫数据时,可以极大地提高效率。可如下读取JSON文件:

# data.json为同目录下的一个文件
pd.read_json('data.json')

可以解析一个JSON字符串,以下是从HTTP服务检测到的设备信息:

jdata='{"res":{"model":"iPhone","browser":"Safari","version":"604.1"},"status":200}'
pd.read_json(jdata)
'''
            res  status
browser  Safari     200
model    iPhone     200
version   604.1     200
'''

Pandas还提供了pd.json_normalize(data)方法来读取半结构化的JSON数据。

3.1.4 HTML

pd.read_html()函数可以接受HTML字符串、HTML文件、URL,并将HTML中的<table>标签表格数据解析为DataFrame。如返回有多个df的列表,则可以通过索引取第几个。如果页面里只有一个表格,那么这个列表就只有一个DataFrame。此方法是Pandas提供的一个简单实用的实现爬虫功能的方法。

dfs = pd.read_html('https://www.gairuo.com/p/pandas-io')
dfs[0] # 查看第一个df
# 读取网页文件,第一行为表头
dfs = pd.read_html('data.html', header=0)
# 第一列为索引
dfs = pd.read_html(url, index_col=0)

如果一个网页表格很多,可以指定元素来获取:

# id='table'的表格,注意这里仍然可能返回多个
dfs1 = pd.read_html(url, attrs={'id': 'table'})
# dfs1[0]
# class='sortable'
dfs2 = pd.read_html(url, attrs={'class': 'sortable'})

常用的参数与read_csv的基本相同。

3.1.5 剪贴板

剪贴板(Clipboard)是操作系统级的一个暂存数据的地方,它保存在内存中,可以在不同软件之间传递,非常方便。Pandas支持读取剪贴板中的结构化数据,这就意味着我们不用将数据保存成文件,而可以直接从网页、Excel等文件中复制,然后从操作系统的剪贴板中读取,非常方便。

'''
  x y z
a 1 2 3
b 4 5 6
c 7 8 9
'''

# 复制上边的数据,然后直接赋值
cdf = pd.read_clipboard()

变量cdf就是上述文本的DataFrame结构数据。read_clipboard的参数使用与read_csv完全一样。

3.1.6 SQL

Pandas需要引入SQLAlchemy库来支持SQL,在SQLAlchemy的支持下,它可以实现所有常见数据库类型的查询、更新等操作。Pandas连接数据库进行查询和更新的方法如下。

        read_sql_table(table_name, con[, schema, …]):把数据表里的数据转换成DataFrame。        

        read_sql_query(sql, con[, index_col, …]):用sql查询数据到DataFrame中。

        read_sql(sql, con[, index_col, …]):同时支持上面两个功能。

        DataFrame.to_sql(self, name, con[, schema, …]):把记录数据写到数据库里。

以下是一些代码示例:

# 需要安装SQLAlchemy库
from sqlalchemy import create_engine
# 创建数据库对象,SQLite内存模式
engine = create_engine('sqlite:///:memory:')
# 取出表名为data的表数据
with engine.connect() as conn, conn.begin():
    data = pd.read_sql_table('data', conn)

# data
# 将数据写入
data.to_sql('data', engine)
# 大量写入
data.to_sql('data_chunked', engine, chunksize=1000)
# 使用SQL查询
pd.read_sql_query('SELECT * FROM data', engine)

3.2 读取CSV

3.2.1 语法

基本语法如下,pd为导入Pandas模块的别名:

pd.read_csv(filepath_or_buffer: Union[str, pathlib.Path, IO[~AnyStr]],
            sep=',', delimiter=None, header='infer', names=None, index_col=None,
            usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True,
            dtype=None, engine=None, converters=None, true_values=None,
            false_values=None, skipinitialspace=False, skiprows=None,
            skipfooter=0, nrows=None, na_values=None, keep_default_na=True,
            na_filter=True, verbose=False, skip_blank_lines=True,
            parse_dates=False, infer_datetime_format=False,
            keep_date_col=False, date_parser=None, dayfirst=False,
            cache_dates=True, iterator=False, chunksize=None,
            compression='infer', thousands=None, decimal: str = '.',
            lineterminator=None, quotechar='"', quoting=0,
            doublequote=True, escapechar=None, comment=None,
            encoding=None, dialect=None, error_bad_lines=True,
            warn_bad_lines=True, delim_whitespace=False,
            low_memory=True, memory_map=False, float_precision=None)

一般情况下,会将读取到的数据返回一个DataFrame,当然按照参数的要求会返回指定的类型。

3.2.2 数据内容

filepath_or_buffer为第一个参数,没有默认值,也不能为空,根据Python的语法,第一个参数传参时可以不写参数名。可以传文件路径:

# 支持文件路径或者文件缓冲对象
# 本地相对路径
pd.read_csv('data/data.csv') # 注意目录层级
pd.read_csv('data.csv') # 如果文件与代码文件在同一目录下
pd.read_csv('data/my/my.data') # CSV文件的扩展名不一定是.csv
# 本地绝对路径
pd.read_csv('/user/gairuo/data/data.csv')
# 使用URL
pd.read_csv('https://www.gairuo.com/file/data/dataset/GDP-China.csv')

需要注意的是,Mac中和Windows中路径的写法不一样,上例是Mac中的写法,Windows中的相对路径和绝对路径需要分别换成类似'data\data.csv'和'E:\data\data.csv'的形式。另外,路径尽量不要使用中文,否则程序容易报错,这意味着你存放数据文件的目录要尽量用英文命名。

可以传数据字符串,即CSV中的数据字符以字符串形式直接传入:

from io import StringIO
data = ('col1,col2,col3\n'
        'a,b,1\n'
        'a,b,2\n'
        'c,d,3')

pd.read_csv(StringIO(data))
pd.read_csv(StringIO(data), dtype=object)

也可以传入字节数据:

from io import BytesIO
data = (b'word,length\n'
        b'Tr\xc3\xa4umen,7\n'
        b'Gr\xc3\xbc\xc3\x9fe,5')

pd.read_csv(BytesIO(data))

3.2.3 分隔符

sep参数是字符型的,代表每行数据内容的分隔符号,默认是逗号,另外常见的还有制表符(\t)、空格等,根据数据的实际情况传值。

# 数据分隔符默认是逗号,可以指定为其他符号
pd.read_csv(data, sep='\t') # 制表符分隔tab
pd.read_table(data) # read_table 默认是制表符分隔tab
pd.read_csv(data, sep='|') # 制表符分隔tab
pd.read_csv(data,sep="(?<!a)\|(?!1)", engine='python') # 使用正则表达式

pd.read_csv还提供了一个参数名为delimiter的定界符,这是一个备选分隔符,是sep的别名,效果和sep一样。如果指定该参数,则sep参数失效。

3.2.4 表头

header参数支持整型和由整型组成的列表,指定第几行是表头,默认会自动推断把第一行作为表头。

pd.read_csv(data, header=0) # 第一行
pd.read_csv(data, header=None) # 没有表头
pd.read_csv(data, header=[0,1,3]) # 多层索引MultiIndex

注意,如果skip_blank_lines=True,header参数将忽略空行和注释行,因此header=0表示第一行数据而非文件的第一行。

3.2.5 列名

names用来指定列的名称,它是一个类似列表的序列,与数据一一对应。如果文件不包含列名,那么应该设置header=None,列名列表中不允许有重复值。

pd.read_csv(data, names=['列1', '列2']) # 指定列名列表
pd.read_csv(data, names=['列1', '列2'], header=None)

3.2.6 索引

index_col用来指定索引列,可以是行索引的列编号或者列名,如果给定一个序列,则有多个行索引。Pandas不会自动将第一列作为索引,不指定时会自动使用以0开始的自然索引。

# 支持int、str、int序列、str序列、False,默认为None
pd.read_csv(data, index_col=False) # 不再使用首列作为索引
pd.read_csv(data, index_col=0) # 第几列是索引
pd.read_csv(data, index_col='年份') # 指定列名
pd.read_csv(data, index_col=['a','b']) # 多个索引
pd.read_csv(data, index_col=[0, 3]) # 按列索引指定多个索引

3.2.7 使用部分列

如果只使用数据的部分列,可以用usecols来指定,这样可以加快加载速度并降低内存消耗。

# 支持类似列表的序列和可调用对象
# 读取部分列
pd.read_csv(data, usecols=[0,4,3]) # 按索引只读取指定列,与顺序无关
pd.read_csv(data, usecols=['列1', '列5']) # 按列名,列名必须存在
# 指定列顺序,其实是df的筛选功能
pd.read_csv(data, usecols=['列1', '列5'])[['列5', '列1']]
# 以下用callable方式可以巧妙指定顺序,in后面的是我们要的顺序
pd.read_csv(data, usecols=lambda x: x.upper() in ['COL3', 'COL1'])

3.2.8 返回序列

将squeeze设置为True,如果文件只包含一列,则返回一个Series,如果有多列,则还是返回DataFrame。

# 布尔型,默认为False
# 下例只取一列,会返回一个Series
pd.read_csv(data, usecols=[0], squeeze=True)
# 有两列则还是df
pd.read_csv(data, usecols=[0, 2], squeeze=True)

3.2.9 表头前缀

如果原始数据没有列名,可以指定一个前缀加序数的名称,如n0、n1,通过prefix参数指定前缀。

# 格式为字符型str
# 表头为c_0、c_2
pd.read_csv(data, prefix='c_', header=None)

3.2.10 处理重复列名

如果该参数为True,当列名有重复时,解析列名将变为X, X.1, …, X.N,而不是X,…, X。如果该参数为False,那么当列名中有重复时,前列将会被后列覆盖。

# 布尔型,默认为True
data = 'a,b,a\n0,1,2\n3,4,5'
pd.read_csv(StringIO(data), mangle_dupe_cols=True)
# 表头为a b a.1
# False会报ValueError错误

3.2.11 数据类型

dtype可以指定各数据列的数据类型,后续章节会专门介绍。

# 传入类型名称,或者以列名为键、以指定类型为值的字典
pd.read_csv(data, dtype=np.float64) # 所有数据均为此数据类型
pd.read_csv(data, dtype={'c1':np.float64, 'c2': str}) # 指定字段的类型
pd.read_csv(data, dtype=[datetime, datetime, str, float]) # 依次指定

3.2.12 引擎

使用的分析引擎可以选择C或Python。C语言的速度最快,Python语言的功能最为完善,一般情况下,不需要另行指定。

# 格式为engine=None,其中可选值有{'c', 'python'}
pd.read_csv(data, engine='c')

3.2.13 列数据处理

使用converters参数对列的数据进行转换,参数中指定列名与针对此列的处理函数,最终以字典的形式传入,字典的键可以是列名或者列的序号。

# 字典格式,默认为None
data = 'x,y\na,1\nb,2'
def foo(p):
    return p+'s'
# x应用函数,y使用lambda
pd.read_csv(StringIO(data), converters={'x': foo,
                                        'y': lambda x: x*3})
# 使用列索引
pd.read_csv(StringIO(data),
            converters={0: foo, 1: lambda x: x*3})

3.2.14 真假值转换

使用true_values和false_values将指定的文本内容转换为True或False,可以用列表指定多个值。

# 列表,默认为None
data = ('a,b,c\n1,Yes,2\n3,No,4')
pd.read_csv(StringIO(data),
            true_values=['Yes'], false_values=['No'])

3.2.15 跳过指定行

如下跳过需要忽略的行数(从文件开始处算起)或需要忽略的行号列表(从0开始):

# 类似列表的序列或者可调用对象
# 跳过前三行
pd.read_csv(data, skiprows=2)
# 跳过前三行
pd.read_csv(data, skiprows=range(2))
# 跳过指定行
pd.read_csv(data, skiprows=[24,234,141])
# 跳过指定行
pd.read_csv(data, skiprows=np.array([2, 6, 11]))
# 隔行跳过
pd.read_csv(data, skiprows=lambda x: x % 2 != 0)

尾部跳过,从文件尾部开始忽略,C引擎不支持。

# int类型, 默认为0
pd.read_csv(filename, skipfooter=1) # 最后一行不加载

skip_blank_lines指定是否跳过空行,如果为True,则跳过空行,否则数据记为NaN。

# 布尔型,默认为True
# 不跳过空行
pd.read_csv(data, skip_blank_lines=False)

如果skip_blank_lines=True,header参数将忽略空行和注释行,因此header=0表示第一行数据而非文件的第一行。

3.2.16 读取指定行

nrows参数用于指定需要读取的行数,从文件第一行算起,经常用于较大的数据,先取部分进行代码编写。

# int类型,默认为None
pd.read_csv(data, nrows=1000)

3.2.17 空值替换

na_values参数的值是一组用于替换NA/NaN的值。如果传参,需要指定特定列的空值。以下值默认会被认定为空值:

['-1.#IND', '1.#QNAN', '1.#IND', '-1.#QNAN',
 '#N/A N/A', '#N/A', 'N/A', 'n/a', 'NA',
 '#NA', 'NULL', 'null', 'NaN', '-NaN',
 'nan', '-nan', '']

使用na_values时需要关注下面keep_default_na的配合使用和影响:

# 可传入标量、字符串、类似列表序列和字典,默认为None
# 5和5.0会被认为是NaN
pd.read_csv(data, na_values=[5])
# ?会被认为是NaN
pd.read_csv(data, na_values='?')
# 空值为NaN
pd.read_csv(data, keep_default_na=False, na_values=[""])
# 字符NA和字符0会被认为是NaN
pd.read_csv(data, keep_default_na=False, na_values=["NA", "0"])
# Nope会被认为是NaN
pd.read_csv(data, na_values=["Nope"])
# a、b、c均被认为是NaN,等于na_values=['a','b','c']
pd.read_csv(data, na_values='abc')
# 指定列的指定值会被认为是NaN
pd.read_csv(data, na_values={'c':3, 1:[2,5]})

3.2.18 保留默认空值

分析数据时是否包含默认的NaN值,是否自动识别。如果指定na_values参数,并且keep_default_na=False,那么默认的NaN将被覆盖,否则添加。keep_default_na和na_values的关系见表3-2。

说明,如果na_filter为False(默认为True),那么keep_default_na和na_values参数均无效。

# 布尔型,默认为True
# 不自动识别空值
pd.read_csv(data, keep_default_na=False)

3.2.19 日期时间解析

# 解析时间的函数名,默认为None
# 指定时间解析库,默认是dateutil.parser.parser
date_parser = pd.io.date_converters.parse_date_time
date_parser = lambda x: pd.to_datetime(x, utc=True, format='%d%b%Y')
date_parser = lambda d: pd.datetime.strptime(d, '%d%b%Y')
# 使用
pd.read_csv(data, parse_dates=['年份'], date_parser=date_parser)

parse_dates参数用于对时间日期进行解析

# 布尔型、整型组成的列表、列表组成的列表或者字典,默认为False
pd.read_csv(data, parse_dates=True) # 自动解析日期时间格式
pd.read_csv(data, parse_dates=['年份']) # 指定日期时间字段进行解析
# 将第1、4列合并解析成名为“时间”的时间类型列
pd.read_csv(data, parse_dates={'时间':[1,4]})

如果infer_datetime_format被设定为True并且parse_dates可用,那么Pandas将尝试转换为日期类型。

# 布尔型,默认为False
pd.read_csv(data, parse_dates=True, infer_datetime_format=True)

如果用上文中的parse_dates参数将多列合并并解析成一个时间列,设置keep_date_col的值为True时,会保留这些原有的时间组成列;如果设置为False,则不保留这些列。

# 布尔型,默认为False
pd.read_csv(data, parse_dates=[[1, 2], [1, 3]], keep_date_col=True)

对于DD/MM格式的日期类型,如日期2020-01-06,如果dayfirst=True,则会转换成2020-06-01。

# 布尔型,默认为False
pd.read_csv(data, dayfirst=True, parse_dates=[0])

cache_dates如果为True,则使用唯一的转换日期缓存来应用datetime转换。解析重复的日期字符串,尤其是带有时区偏移的日期字符串时,可能会大大提高速度。

# 布尔型,默认为True
pd.read_csv(data, cache_dates=False)

3.2.20 文件处理

以下是一些对读取文件对象的处理方法。iterator参数如果设置为True,则返回一个TextFileReader对象,并可以对它进行迭代,以便逐块处理文件。

# 布尔型,默认为False
pd.read_csv(data, iterator=True)

chunksize指定文件块的大小,分块处理大型CSV文件。

# 整型,默认为None
pd.read_csv(data, chunksize=100000)
# 分块处理大文件
df_iterator = pd.read_csv(file, chunksize=50000)
def process_dataframe(df):
    pass
    return processed_df

for index,df_tmp in enumerate(df_iterator):
    df_processed = process_dataframe(df_tmp)
    if index > 0:
       df_processed.to_csv(path)
    else:
       df_processed.to_csv(path, mode='a', header=False)

compression(压缩格式)用于对磁盘数据进行即时解压缩。如果为“infer”,且filepath_or_buffer是以.gz、.bz2、.zip或.xz结尾的字符串,则使用gzip、bz2、zip或xz,否则不进行解压缩。如果使用zip,则ZIP文件必须仅包含一个要读取的数据文件。设置为None将不进行解压缩。

# 可选值有'infer'、'gzip'、'bz2'、'zip'、'xz'和None,默认为'infer'
pd.read_csv('sample.tar.gz', compression='gzip')

encoding(编码)指定字符集类型,通常指定为'utf-8'。

# 字符型,默认为None
pd.read_csv('gairuo.csv', encoding='utf8')
pd.read_csv("gairuo.csv",encoding="gb2312") # 常见中文

3.2.21 符号

以下是对文件中的一些数据符号进行的特殊识别处理。如下设置千分位分隔符thousands:

# 字符型,默认为None
pd.read_csv('test.csv', thousands=',') # 逗号分隔

小数点decimal,识别为小数点的字符。

# 字符串,默认为'.'
pd.read_csv(data, decimal=",")

行结束符lineterminator,将文件分成几行的字符,仅对C解析器有效。

# 长度为1的字符串,默认为None
data = 'a,b,c~1,2,3~4,5,6'
pd.read_csv(StringIO(data), lineterminator='~')

引号quotechar,用于表示引用数据的开始和结束的字符。引用的项目可以包含定界符,它将被忽略。

# 长度为1的字符串
pd.read_csv(file, quotechar='"')

在csv模块中,数据可能会用引号等字符包裹起来,quoting参数用来控制识别字段的引号模式,它可以是Python csv模块中的csv.QUOTE_*常量,也可以传入对应的数字。各个传入值的意义如下。

0或csv.QUOTE_MINIMAL:仅特殊字段有引号。

1或csv.QUOTE_ALL:所有字段都有引号。

2或csv.QUOTE_NONNUMERIC:所有非数字字段都有引号。

3或csv.QUOTE_NONE:所有字段都没有引号。

如果使用csv模块,则需要事先引入csv模块。

# 整型或者csv.QUOTE_*实例, 默认为0
import csv
pd.read_csv('input_file.csv', quoting=csv.QUOTE_NONE)

双引号doublequote,当单引号已经被定义,并且quoting参数不是QUOTE_NONE的时候,使用双引号表示将引号内的元素作为一个元素使用。

# 布尔型,默认为True
import csv
pd.read_csv('data.csv', quotechar='"', doublequote=True, quoting=csv.QUOTE_NONNUMERIC)

escapechar可以传入一个转义符,用于过滤数据中的该转入符。比如,如果一行用双引号包裹着的数据中有换行符,用以下代码可以过滤其中的换行符。

# 长度为1的转义字符串,默认为None
pd.read_csv(StringIO(data), escapechar='\n', encoding='utf-8')

注释标识comment,指示不应分析行的部分。如果在一行的开头找到该标识,则将完全忽略该行。此参数必须是单个字符。像空行一样(只要skip_blank_lines =True),注释的行将被参数header忽略,而不是被skiprows忽略。例如,如果comment ='#',则解析header=0的'#empty \ na,b,c \ n1,2,3'会将'a,b,c'视为header。

# 字符串,默认为None
s = '# notes\na,b,c\n# more notes\n1,2,3'
pd.read_csv(StringIO(s), sep=',', comment='#', skiprows=1)

空格分隔符delim_whitespace,指定是否将空格(例如''或'\ t')用作分隔符,等效于设置sep ='\s+'。如果此选项设置为True,则不应该为delimiter参数传递任何内容。

# 布尔型,默认为False
pd.read_csv(StringIO(data), delim_whitespace=False)

————————————————————————————

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值